-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = '	TEST'
VERSION = '1.0.0'

log.info("main", PROJECT, VERSION)

-- sys库是标配
_G.sys = require("sys")

if wdt then
    --添加硬狗防止程序卡死，在支持的设备上启用这个功能
    wdt.init(15000)--初始化watchdog设置为15s
    sys.timerLoopStart(wdt.feed, 10000)--10s喂一次狗
end
--------------------------单灯LED闪烁实验--------------------------------
--[[
local LED1 = gpio.setup(pin.PB08, 0) -- PB0输出模式
sys.taskInit(function()
    while 1 do
        log.info("LED开启")
        LED1(0)
        sys.wait(1000)
        log.info("LED关闭")
        LED1(1)
        sys.wait(1000)
    end
end)
--]]
--------------------------- 跑马灯实验-----------------------------------

-- 下面的GPIO引脚编号，请根据实际需要进行更改！
-- Air101开发板的3个LED分别为 PB08/PB09/PB10
-- Air103开发板的3个LED分别为 PB24/PB25/PB26
-- Air105开发板的3个LED分别为 PD14/PD15/PC3
local LEDA = gpio.setup(pin.PB08, 0, gpio.PULLUP) -- PB8输出模式,内部上拉
local LEDB = gpio.setup(pin.PB09, 0, gpio.PULLUP) -- PB9输出模式,内部上拉
local LEDC = gpio.setup(pin.PB10, 0, gpio.PULLUP) -- PB10输出模式,内部上拉
sys.taskInit(function()
    local count = 0
    while 1 do
        sys.wait(500)
        -- 一闪一闪亮晶晶
        LEDA(count % 3 == 0 and 1 or 0)
        LEDB(count % 3 == 1 and 1 or 0)
        LEDC(count % 3 == 2 and 1 or 0)
        log.info("gpio", "Go Go Go", count, rtos.bsp())
        count = count + 1
    end
end)
--]]
--------------------------------- 按键实验-------------------------------
--[[
local button_timer_outtime = 10 --按键定时器: 10ms
local button_shake_time = 1     --按键消抖时间: button_shake_time*button_timer_outtime
local button_long_time = 100    --按键长按时间: button_shake_time*button_timer_outtime
local button_state = 0 --按键状态（松开0，按下1）
local button_cont = 0
local LEDA = gpio.setup(pin.PB08, 0, gpio.PULLUP) -- PB8输出模式,内部上拉
button = gpio.setup(pin.PA00,	--button获取电平的闭包，button()表示状态
function()						--中断模式
	LEDA(1-gpio.get(pin.PB08))
end,
gpio.PULLUP,gpio.FALLING)  --按键按下接地，因此需要上拉
sys.taskInit(function()
    while 1 do
		if button() == 0 then   --按键当前时刻为低电平（被按下） 此处根据上下拉选择0/1
			button_cont = button_cont + 1   --计数值加一
			if button_cont > button_long_time then  --计数次数大于长按阈值
				print("long passing")
			end
		else --按键当前时刻电平与状态不符
			if button_cont < button_shake_time then  --计数次数小于抖动阈值（视为抖动）
			else
				if button_cont < button_long_time then  --计数次数小于长按阈值
					print("pass")     --正常按键情况
				else
					print("long pass")  --长按情况
				end
			end
			button_cont = 0   --计数清零
			button_state = 0  --松开状态
		end
		sys.wait(button_timer_outtime)
    end
end)
--]]

--另一种写法
--[[
button_timer = sys.timerLoopStart(function()
	if button() == 0 then   --按键当前时刻为低电平（被按下） 此处根据上下拉选择0/1
		button_cont = button_cont + 1   --计数值加一
		if button_cont > button_long_time then  --计数次数大于长按阈值
			print("long pass")
		end
	else --按键当前时刻电平与状态不符
		if button_cont < button_shake_time then  --计数次数小于抖动阈值（视为抖动）
		else
			if button_cont < button_long_time then  --计数次数小于长按阈值
				print("pass")     --正常按键情况
			else
				print("long pass......")  --长按情况
			end
		end
		button_cont = 0   --计数清零
		button_state = 0  --松开状态
	end
end,button_timer_outtime) --每次轮询间隔
--]]
------------------------------- 按键实验（V2.0）-----------------------------
--[[
local button_timer_outtime = 10 --按键定时器: 10ms
local button_shake_time = 1     --按键消抖时间: button_shake_time*button_timer_outtime
local button_long_time = 100    --按键长按时间: button_shake_time*button_timer_outtime
local button_state = 0          --按键状态（松开0，按下1）
local button_cont = 0

local count_state = false        --s是否处在两次按下间计数状态
local button_cont_release = 0    
local button_release_time = 20    --最长连按时间: button_release_time*button_timer_outtime

button = gpio.setup(pin.PA00,	--button获取电平的闭包，button()表示状态
function() end,						--中断模式
gpio.PULLUP,gpio.FALLING)  --按键按下接地，因此需要上拉

sys.taskInit(function()
    while 1 do
		if button() == 0 then   --按键当前时刻为低电平（被按下） 此处根据上下拉选择0/1
			button_cont = button_cont + 1   --计数值加一
			if button_cont > button_long_time then  --计数次数大于长按阈值
				log.info("long passing")
			end
		else --按键当前时刻电平与状态不符
			if button_cont < button_shake_time then  --计数次数小于抖动阈值（视为抖动）
                if button_cont_release > button_release_time then
                    button_cont_release = 0
                    count_state = false
                    log.info("one pass")     --正常按键情况
                end
			else
				if button_cont < button_long_time then  --计数次数小于长按阈值
                    if button_cont_release <= button_release_time and count_state == true then
                        button_cont_release = 0
                        log.info("two pass")     --按键连按情况
                    end
                    count_state = not count_state
				else
					log.info("long pass")  --长按情况
				end
			end
			button_cont = 0   --计数清零
			button_state = 0  --松开状态
            if button_cont_release <= button_release_time and count_state == true then
                button_cont_release = button_cont_release + 1   --松开计算值加一
            end
		end
		sys.wait(button_timer_outtime)
    end
end)
--]]
----------------------------PWM控制LED灯实验-----------------------------
--[[
-- Air101
-- PWM4 --> PA7
-- PWM3 --> PB3
-- PWM2 --> PB2
-- PWM1 --> PB1
-- PWM0 --> PB0
-- https:--wiki.luatos.com/api/pwm.html
sys.taskInit(function()
    while 1 do
        -- 仿呼吸灯效果（需要外接LED）
        log.info("pwm", ">>>>>")
        for i = 100,1,-1 do 
            pwm.open(4, 1000, 100-i) -- 打开PWM4, 频率1000hz, 占空比0-100
            -- pwm.open(5, 1000, 31, 0, 256) -- 打开PWM4, 频率1000Hz, 单次输出，分频（占空比）为 31/256
            sys.wait(20)
        end
        sys.wait(1000)
        for i = 100,1,-1 do 
            pwm.open(4, 1000, i)
            sys.wait(20)
        end
        sys.wait(1000)
    end
end)
--]]
--------------------------PWM控制舵机实验--------------------------------
--[[
-- SG90舵机 周期20ms  脉冲宽度0.5ms（0°）~2.5ms（180°）
-- 直接连开发板需要接5V
-- Air101不支持分频系数为1000
sys.taskInit(function()
    while 1 do
        log.info("Servo", ">>>>>")
        for i = 8,32,1 do 
            pwm.open(4, 50, i, 0 ,256) -- 打开PWM4, 频率50hz, 单次输出，分频（占空比）为 i/256
            sys.wait(20)
        end
        sys.wait(1000)
        for i = 32,8,-1 do 
            pwm.open(4, 50, i, 0 ,256) -- 打开PWM4, 频率50hz, 单次输出，分频（占空比）为 i/256
            sys.wait(20)
        end
        sys.wait(1000)
        pwm.open(4, 50, 32, 0 ,256)
        sys.wait(2000)
        pwm.open(4, 50, 8, 0 ,256)
        sys.wait(2000)
    end
end)
--]]
---------------------------UART串口实验----------------------------------
--[[
-- 实验过程：使用串口命令on/off控制LED灯的亮灭，串口接受的数据将发送返回
-- 串口库文档：https:--wiki.luatos.com/api/uart.html
local LED1 = gpio.setup(pin.PB08, 0) -- PB0输出模式

local uartid,sendQueue = 1,{}   -- 根据实际设备选取不同的uartid,串口读缓冲区

-- 串口超时，串口准备好后发布的消息
-- 例子是100ms，按需求改
local uartimeout, recvReady = 100, "UART_RECV"
--初始化
uart.setup(
    uartid,--串口id
    115200,--波特率
    8,--数据位
    1--停止位
)

uart.on(uartid, "receive", function(id, len) -- 注册串口中断服务函数，中断类型为接收中断，function内固定参数
    local s
    while true do -- 保证读完不能丢包
        s = uart.read(id, len)
        if #s == 0 then break end -- #s是取字符串的长度,等于0表示接受完成，退出
        table.insert(sendQueue, s)  -- 读取到的数据传入串口读缓冲区
    end
    sys.timerStart(sys.publish, uartimeout, recvReady)  -- 接收完成uartimeout时间后发布recvReady消息
end)

-- publish和subscribe结合使用，订阅到发布的消息后执行函数
-- 向串口发送收到的字符串
sys.subscribe(recvReady, function()      
    --拼接所有收到的数据
    local str = table.concat(sendQueue)
    -- 串口的数据读完后清空缓冲区
    sendQueue = {}
    --注意打印会影响运行速度，调试完注释掉
    --log.info("uartTask.read length", #str, str:sub(1,100))
    uart.write(uartid,str) --回复
    if str == "on" then 
        LED1(1)
    elseif str == "off" then
        LED1(0)
    end
    --在这里处理接收到的数据，这是例子
end)

-- 并非所有设备都支持sent事件(实测Air101可实现该事件中断)
uart.on(uartid, "sent", function(id)  -- 注册串口中断服务函数，中断类型为发送完成中断
    log.info("uart"..id, "sent")
end)
--]]
--------------------------------ADC实验---------------------------------
--[[
-- ADC库文档：https:--wiki.luatos.com/api/adc.html
-- Air101  输入电压范围0~2.4v
-- ADC编号   功能
--   0     ADC0->PA1  
--   1     ADC1->PA4  
--   10    CPU温度
--   11    内部电压
local adc0,adc_temp,vbat
local LED1 = gpio.setup(pin.PB08, 0) -- PB0输出模式
sys.taskInit(function()
    while 1 do
        adc.open(0)  -- 模块上的ADC0脚-PA1, 0~2.4v,不要超过范围使用!!!   返回两个int值，原始值和电压（mv）/温度值（mC）
        adc.open(10) -- CPU温度
        adc.open(11) -- VBAT电压,最新代码才支持
        sys.wait(500)

        adc0 = {adc.read(0)}
        adc_temp = {adc.read(10)}
        vbat = {adc.read(11)}

        if adc_temp[2] > 40000 then
            LED1(1)
        else
            LED1(0)
        end

        log.info("adc", "adc0: ".. (adc0[2]/1000).."V")
        log.info("adc", "temp: ".. (adc_temp[2]/1000).."°C")
        log.info("adc", "vbat: ".. (vbat[2]/1000).."V")
        -- 使用完毕后关闭,可以使得休眠电流更低.
        adc.close(0)
        adc.close(1)
        adc.close(10)
        adc.close(11)
        sys.wait(500)     
    end
end)
--]]
-------------------------I2C实验（AT24Cxx系列EEPROM)---------------------
--[[
--PA1为SCL引脚，PA4为SDA引脚
--https://blog.csdn.net/name_longming/article/details/106151754
--https://blog.csdn.net/u013427264/article/details/42708855?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2.pc_relevant_default
--注意I2C地址为七位地址
local AT24C02_ADDRESS   =   0x50   --A0，A1，A2接地为0，地址为1010 000 所以芯片选址为0x50
sys.taskInit(function()
    -- 初始化i2c，使用id为0
    if i2c.setup(0, i2c.FAST, AT24C02_ADDRESS) == 1 then     
        log.info("存在 i2c0")
    else
        i2c.close(0) -- 关掉
    end
    local addr = 0x05  -- AT24C02是一个2K位串行CMOS E2PROM，内部含有256个8位字节 地址0x00-0xFF
    local i = 0x01
    while 1 do
        log.info("写入：", i2c.writeReg(0, AT24C02_ADDRESS, addr, string.char(i, i+1)))  --写入两个字节 最多一次7个
        log.info("写入的值为", i,i+1)
        sys.wait(1000) -- 等待延时
        log.info("读取的数为：", (i2c.readReg(0, AT24C02_ADDRESS, addr, 2)):toHex()) -- 打印读取数据采集结果 读两个字节
        sys.wait(1000)
    end
end)
--]]
-------------------------I2C实验（AHT20+BMP280库实现)--------------------
--[[ 
local bmx = require "bmx"
i2cid = 0
--i2c_speed = i2c.SLOW
i2c_speed = i2c.FAST
sys.taskInit(function()
    i2c.setup(i2cid,i2c_speed)
    bmx.init(i2cid)--初始化,传入i2c_id
    while 1 do
        local bmx_data = bmx.get_data()
        if bmx_data.hum then
            log.info("bmp180湿度:"..(bmx_data.hum).."%")       --bmp180不支持
        end
        if bmx_data.temp then
            log.info("bmp180温度:"..(bmx_data.temp).."℃")
        end
        if bmx_data.press then
            log.info("bmp180气压:"..(bmx_data.press).."hPa")
        end
        if bmx_data.high then
            log.info("bmp180高度:"..(bmx_data.high).."m")
        end
        log.info("--------------------------------")
        sys.wait(1000)
    end
end)


local aht10 = require "aht10"    --AHT20可以用AHT10的这个，但实际有出入的,20初始化指令为BE，10初始化指令为0xE1（aht10.lua中修改该处即可）
--i2cid = 0
--i2c_speed = i2c.FAST
sys.taskInit(function()
    i2c.setup(i2cid,i2c_speed)
    aht10.init(i2cid)--初始化,传入i2c_id
    while 1 do
        local aht10_data = aht10.get_data()
        log.info("AHT20湿度:"..(aht10_data.RH*100).."%")
        log.info("AHT20温度:"..(aht10_data.T).."℃")
        sys.wait(1000)
    end
end) 
--]]
------------------------------I2C实验（AHT20温湿度)----------------------
--[[
--https://zhuanlan.zhihu.com/p/312206749
--初始化命令 ('1011’1110') ，即0xBE；
--测量温湿度命令('1010’1100')，即0xAC；
--软复位命令('1011’1010')，即0xBA。
--AHT20_DEVICE_ADDR   0x38
--AHT20_READ_ADDR     ((0x38<<1)|0x1)
--AHT20_WRITE_ADDR    ((0x38<<1)|0x0)

--此处没有使用状态位，若使用以保证正常运行可参照aht10.lua文件
local AHT20_ADDRESS           =   0x38
---器件所用地址
local AHT20_INIT                  =   0xBE --初始化命令
local AHT20_MEASURE               =   0xAC --触发测量命令
local AHT20_SOFT_RESET            =   0xBA --软复位命令,软复位所需时间不超过20毫秒.
local AHT20_STATE                 =   0x71 --状态字.详见文档

local raw_data={Srh=nil,St=nil}
local aht20_data={RH=nil,T=nil}

sys.taskInit(function()
    -- 初始化i2c，使用id为0
    if i2c.setup(0, i2c.FAST, AHT10_ADDRESS) == 1 then     
        log.info("存在 i2c0")
    else
        i2c.close(0) -- 关掉
    end
    sys.wait(40)--40 毫秒等待设备稳定
    i2c.send(0, AHT20_ADDRESS, AHT20_SOFT_RESET)--软复位
    sys.wait(20)
    i2c.send(0, AHT20_ADDRESS, string.char(AHT20_INIT)) -- 发送初始化命令
    while 1 do
        i2c.send(0, AHT20_ADDRESS, {AHT20_MEASURE, 0x33, 0x00})  --直接发送 0xAC命令(触发测量)，此命令参数有两个字节，第一个字节为 0x33，第二个字节为0x00
        sys.wait(80)--等待80毫秒以上保证测量完成，状态位忙状态Bit[7]为0，然后可以读取六个字节(发0X71即可以读取)。
        local data = i2c.recv(0, AHT20_ADDRESS, 6) -- 从i2c0读取6个字节的数据
        local _, data1, data2, data3, data4, data5, data6 = pack.unpack(data, "b6")  --解包为6个字符型
        
        raw_data.Srh = bit.bor(bit.bor(bit.rshift(data4, 4), bit.lshift(data3, 4)),bit.lshift(data2, 12))   --湿度获取原始值
        raw_data.St = bit.bor(bit.bor(bit.lshift(bit.band(data4, 0x0f), 16), bit.lshift(data5, 8)), data6)   --温度获取原始值

        aht20_data.RH = raw_data.Srh/1048576     --湿度计算值
        aht20_data.T = raw_data.St/1048576*200-50   --温度计算值
        log.info("温度为："..(aht20_data.RH*100).."%","温度为"..(aht20_data.T).."℃")
        sys.wait(500)
    end
end)
--]]
----------------------未实验：I2C 实验(SHT20温湿度传感器)-----------------
--[[
-- 启动个task, 定时查询SHT20的数据
sys.taskInit(function()

    local tmp,hum -- 原始数据
    local temp,hump -- 真实值

    --1010 000x
    local addr = 0x40
    -- 按实际修改哦
    local id = 0

    log.info("i2c", "initial",i2c.setup(0))

    while true do
        --第一种方式
        i2c.send(id, addr, string.char(0xe3))
        tmp = i2c.recv(id, addr, 2)
        log.info("SHT20", "read tem data", tmp:toHex())

        i2c.send(id, addr, string.char(0xe5))
        hum = i2c.recv(id, addr, 2)
        log.info("SHT20", "read hum data", hum:toHex())
        local _,tval = pack.unpack(tmp,'>H')
        local _,hval = pack.unpack(hum,'>H')
        if tval and hval then
            temp = ((1750*(tval)/65535-450))/10
            hump = ((1000*(hval)/65535))/10
            log.info("SHT20", "temp,humi",temp,hump)
        end
        sys.wait(1000)
    end
end)
--]]
---------------------未实验：I2C 实验(SHT30温湿度传感器)----------------------
--[[
-- 启动个task, 定时查询SHT20的数据
sys.taskInit(function()
    -- sht30的默认i2c地址
    local addr = 0x44
    -- 按实际修改哦
    local id = 0

    log.info("i2c", "initial",i2c.setup(0))

    while true do
        --第一种方式
        i2c.send(id, addr, string.char(0x2C, 0x06))
        sys.wait(5) -- 5ms
        local data = i2c.recv(id, addr, 6)
        log.info("sht30", data:toHex())
        if #data == 6  then
            local _,tval,ccrc,hval,hcrc = pack.unpack(data, ">HbHb")
            -- local cTemp = ((((data:byte(1) * 256.0) + data:byte(2)) * 175) / 65535.0) - 45
            -- local fTemp = (cTemp * 1.8) + 32
            -- local humidity = ((((data:byte(4) * 256.0) + data:byte(5)) * 100) / 65535.0)
            local cTemp = ((tval * 175) / 65535.0) - 45
            -- local fTemp = (cTemp * 1.8) + 32
            local humidity = ((hval * 100) / 65535.0)
            log.info("sht30", cTemp, humidity)
        end
        sys.wait(2000)
    end
end)
--]]
-----------------------------I2C实验（MPU6050库方法)---------------------
--[[
--PA1为SCL引脚，PA4为SDA引脚
--需要添加mpu6xxx.lua文件
--库内容：https:--gitee.com/openLuat/LuatOS/blob/master/script/libs/mpu6xxx/mpu6xxx.lua
--支持mpu6500,mpu6050,mpu9250,icm2068g,icm20608d,自动判断器件id,只需要配置i2c id就可以
--注意:因使用了sys.wait()所有api需要在协程中使用
local mpu6xxx = require "mpu6xxx"
i2cid = 0
i2c_speed = i2c.FAST
sys.taskInit(function()
    i2c.setup(i2cid,i2c_speed)
    mpu6xxx.init(i2cid)--初始化,传入i2c_id
    while 1 do
        sys.wait(100)
        local temp = mpu6xxx.get_temp()--获取温度
        log.info("6050temp", temp)
        local accel = mpu6xxx.get_accel()--获取加速度 单位: mg
        log.info("6050accel", "accel.x",accel.x,"accel.y",accel.y,"accel.z",accel.z)
        local gyro = mpu6xxx.get_gyro()--获取陀螺仪 deg / 10s
        log.info("6050gyro", "gyro.x",gyro.x,"gyro.y",gyro.y,"gyro.z",gyro.z)
    end
end)
--]]
-----------------------------I2C实验(mp6050)----------------------------
--[[
--尚未实现姿态结算方面的功能（DMP能力不足，无法实现,以下代码也仍存在问题）
local Kp = 10.0                         --这里的KpKi是用于调整加速度计修正陀螺仪的速度
local Ki = 0.008
local halfT = 0.1                     --采样周期的一半，用于求解四元数微分方程时计算角增量
local q0, q1, q2, q3 = 1, 0, 0, 0       --初始姿态四元数，由变换四元数公式得来
local exInt, eyInt, ezInt = 0, 0, 0     --当前加计测得的重力加速度在三轴上的分量
local Q_ANGLE = {X = nil, Y= nil, Z= nil}
local GYRO_I = {X = 0, Y= 0, Z= 0}

function IMUupdate( gx,  gy,  gz,  ax,  ay,  az) --g表陀螺仪，a表加计
    local q0temp,q1temp,q2temp,q3temp       --四元数暂存变量，求解微分方程时要用
    local norm                              --矢量的模或四元数的范数
    local vx, vy, vz                        --当前姿态计算得来的重力在三轴上的分量
    local ex, ey, ez                        --当前加计测得的重力加速度在三轴上的分量
                                            --与用当前姿态计算得来的重力在三轴上的分量的误差
    -- 先把这些用得到的值算好
    local q0q0 = q0*q0
    local q0q1 = q0*q1
    local q0q2 = q0*q2
    local q1q1 = q1*q1
    local q1q3 = q1*q3
    local q2q2 = q2*q2
    local q2q3 = q2*q3
    local q3q3 = q3*q3      
    if(ax*ay*az==0) then--加计处于自由落体状态时不进行姿态解算，因为会产生分母无穷大的情况
        return
    end
    norm = math.sqrt(ax*ax + ay*ay + az*az)--单位化加速度计，
    ax = ax /norm-- 这样变更了量程也不需要修改KP参数，因为这里归一化了
    ay = ay / norm
    az = az / norm
    --用当前姿态计算出重力在三个轴上的分量，
    --参考坐标n系转化到载体坐标b系的用四元数表示的方向余弦矩阵第三列即是（博文一中有提到）
    vx = 2*(q1q3 - q0q2)        
    vy = 2*(q0q1 + q2q3)
    vz = q0q0 - q1q1 - q2q2 + q3q3 
    --计算测得的重力与计算得重力间的误差，向量外积可以表示这一误差
    --原因我理解是因为两个向量是单位向量且sin0等于0
    --不过要是夹角是180度呢~这个还没理解
    ex = (ay*vz - az*vy)                                                                   
    ey = (az*vx - ax*vz) 
    ez = (ax*vy - ay*vx) 

    exInt = exInt + ex * Ki     --对误差进行积分
    eyInt = eyInt + ey * Ki
    ezInt = ezInt + ez * Ki
    -- adjusted gyroscope measurements
    gx = gx + Kp*ex + exInt     --将误差PI后补偿到陀螺仪，即补偿零点漂移
    gy = gy + Kp*ey + eyInt
    gz = gz + Kp*ez + ezInt     --这里的gz由于没有观测者进行矫正会产生漂移，表现出来的就是积分自增或自减
    -- 陀螺仪积分
    GYRO_I.X = GYRO_I.X + halfT*2*gx
    GYRO_I.Y = GYRO_I.Y + halfT*2*gy
    GYRO_I.Z = GYRO_I.Z + halfT*2*gz
    --下面进行姿态的更新，也就是四元数微分方程的求解
    q0temp=q0                   --暂存当前值用于计算
    q1temp=q1                   --网上传的这份算法大多没有注意这个问题，在此更正
    q2temp=q2
    q3temp=q3
    --采用一阶毕卡解法，相关知识可参见《惯性器件与惯性导航系统》P212
    q0 = q0temp + (-q1temp*gx - q2temp*gy -q3temp*gz)*halfT
    q1 = q1temp + (q0temp*gx + q2temp*gz -q3temp*gy)*halfT
    q2 = q2temp + (q0temp*gy - q1temp*gz +q3temp*gx)*halfT
    q3 = q3temp + (q0temp*gz + q1temp*gy -q2temp*gx)*halfT
    --单位化四元数在空间旋转时不会拉伸，仅有旋转角度，这类似线性代数里的正交变换
    norm = math.sqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3)
    q0 = q0 / norm
    q1 = q1 / norm
    q2 = q2 / norm
    q3 = q3 / norm
    --四元数到欧拉角的转换，公式推导
    --其中YAW航向角由于加速度计对其没有修正作用，因此此处直接用陀螺仪积分代替
    Q_ANGLE.Z = GYRO_I.Z  -- yaw(还存在问题)
    Q_ANGLE.Y = math.asin(-2 * q1 * q3 + 2 * q0* q2)*57.3 -- pitch
    Q_ANGLE.X = math.atan2(2 * q2 * q3 + 2 * q0 * q1,-2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3 -- roll
end

local i2cslaveaddr = 0x68  -- mpu6050 AD0引脚不接通信地址默认为0x68
local i2cid = 0    -- 初始化i2c，使用设备id为0(id可以自己定义)
local buffer     --读取数值
local temp       --温度
local accel={x=nil,y=nil,z=nil}   --加速度
local gyro={x=nil,y=nil,z=nil}    --陀螺仪

-- MPU6050数值转换系数
local MPU6050_ACCEL_SEN           =   16384
local MPU6050_GYRO_SEN            =   1310
local MPU6050_TEMP_SEN            =   340
local MPU6050_TEMP_OFFSET         =   36.5

-- MPU6050数据寄存器地址（查手册）
local MPU6050_RA_ACCEL_XOUT_H     =   0x3B
local MPU6050_RA_ACCEL_XOUT_L     =   0x3C
local MPU6050_RA_ACCEL_YOUT_H     =   0x3D
local MPU6050_RA_ACCEL_YOUT_L     =   0x3E
local MPU6050_RA_ACCEL_ZOUT_H     =   0x3F
local MPU6050_RA_ACCEL_ZOUT_L     =   0x40
local MPU6050_RA_TEMP_OUT_H       =   0x41
local MPU6050_RA_TEMP_OUT_L       =   0x42
local MPU6050_RA_GYRO_XOUT_H      =   0x43
local MPU6050_RA_GYRO_XOUT_L      =   0x44
local MPU6050_RA_GYRO_YOUT_H      =   0x45
local MPU6050_RA_GYRO_YOUT_L      =   0x46
local MPU6050_RA_GYRO_ZOUT_H      =   0x47
local MPU6050_RA_GYRO_ZOUT_L      =   0x48

-- MPU6050配置寄存器地址（查手册）
local MPU6050_RA_PWR_MGMT_1     =   0x6B	--电源管理，典型值：0x00(正常启用)
local MPU6050_RA_SMPLRT_DIV		=   0x19	--陀螺仪采样率，典型值：0x07(125Hz)
local MPU6050_RA_CONFIG			=   0x1A	--低通滤波频率，典型值：0x06(5Hz)
local MPU6050_RA_GYRO_CONFIG	=   0x1B	--陀螺仪自检及测量范围，典型值：0x18(不自检，2000deg/s)
local MPU6050_RA_ACCEL_CONFIG	=   0x1C	--加速计自检、测量范围及高通滤波频率，典型值：0x01(不自检，2G，5Hz)
local MPU6050_RA_FIFO_EN        =   0x23    --fifo使能
local MPU6050_RA_INT_PIN_CFG    =   0x37    --int引脚有效电平
local MPU6050_RA_INT_ENABLE     =   0x38    --中断使能
local MPU6050_RA_USER_CTRL      =   0x6A    --MPU6050的主/从机模式，典型值：0x00(从机模式)
local MPU6050_RA_PWR_MGMT_1     =   0x6B
local MPU6050_RA_PWR_MGMT_2     =   0x6C

sys.taskInit(function()
    -- 初始化i2c，setup用的是开发板上的i2c引脚，也可以通过i2c.createSoft创建软件i2c对象
    if i2c.setup(i2cid, i2c.FAST, i2cslaveaddr) == 1 then   
        log.info("i2c"..i2cid.." exist")    
    else
        i2c.close(i2cid) -- 关掉
    end

    -- 初始化mpu6050
    i2c.send(i2cid, i2cslaveaddr, {MPU6050_RA_PWR_MGMT_1, 0x80})    --复位
    sys.wait(100)
    i2c.send(i2cid, i2cslaveaddr, {MPU6050_RA_PWR_MGMT_1, 0x00})    --唤醒
    sys.wait(100)
    i2c.send(i2cid, i2cslaveaddr, {MPU6050_RA_SMPLRT_DIV, 0x07})    --陀螺仪采样率，典型值：0x07(125Hz)
    i2c.send(i2cid, i2cslaveaddr, {MPU6050_RA_CONFIG, 0x06})        --低通滤波频率，典型值：0x06(5Hz)
    i2c.send(i2cid, i2cslaveaddr, {MPU6050_RA_GYRO_CONFIG, 0x18})   --陀螺仪自检及测量范围，典型值：0x18(不自检，2000deg/s)
    i2c.send(i2cid, i2cslaveaddr, {MPU6050_RA_ACCEL_CONFIG, 0x01})  --加速计自检、测量范围及高通滤波频率，典型值：0x01(不自检，2G，5Hz)
    --i2c.send(i2cid, i2cslaveaddr, {MPU6050_RA_FIFO_EN, 0x78})     --打开陀螺仪和加速计fifo
    --i2c.send(i2cid, i2cslaveaddr, {MPU6050_RA_INT_ENABLE, 0x00})  --关闭所有中断
    --i2c.send(i2cid, i2cslaveaddr, {MPU6050_RA_USER_CTRL, 0x00})   --I2C主模式关闭
    i2c.send(i2cid, i2cslaveaddr, {MPU6050_RA_PWR_MGMT_1, 0x01})    --设置x轴的pll为参考
    i2c.send(i2cid, i2cslaveaddr, {MPU6050_RA_PWR_MGMT_2, 0x00})    --加速度计与陀螺仪开启
    log.info("mpu6050 init_ok")

    while 1 do
        sys.wait(halfT*2000)

        --温度原始数据获取 
        i2c.send(i2cid, i2cslaveaddr,MPU6050_RA_TEMP_OUT_H)         --获取温度的地址
        buffer = i2c.recv(i2cid, i2cslaveaddr, 2)                   --获取2字节数据
        _, temp = pack.unpack(buffer, ">h")                         --将字符串解包成大端编码short (2字节)
        --计算温度的数据 单位：°C
        temp = temp / MPU6050_TEMP_SEN + MPU6050_TEMP_OFFSET
        log.info("6050temp", temp)

        --陀螺仪测角速度的，加速度传感器测角加速度的，二者数据通过算法就可以得到PITCH、YAW、ROLL角

        --加速度原始数据获取 
        i2c.send(i2cid, i2cslaveaddr,MPU6050_RA_ACCEL_XOUT_H)       --获取X方向加速度的地址
        buffer = i2c.recv(i2cid, i2cslaveaddr, 2)                   
        _,accel.x = pack.unpack(buffer,">h")
        i2c.send(i2cid, i2cslaveaddr,MPU6050_RA_ACCEL_YOUT_H)       --获取Y方向加速度的地址
        buffer = i2c.recv(i2cid, i2cslaveaddr, 2)                   
        _,accel.y = pack.unpack(buffer,">h")
        i2c.send(i2cid, i2cslaveaddr,MPU6050_RA_ACCEL_ZOUT_H)       --获取Z方向加速度的地址
        buffer = i2c.recv(i2cid, i2cslaveaddr, 2)                   
        _,accel.z = pack.unpack(buffer,">h")
        --计算加速度计的数据 单位: mg
        accel.x = accel.x*1000/MPU6050_ACCEL_SEN
        accel.y = accel.y*1000/MPU6050_ACCEL_SEN
        accel.z = accel.z*1000/MPU6050_ACCEL_SEN
        log.info("6050accel", "accel.x",accel.x,"accel.y",accel.y,"accel.z",accel.z)

        --陀螺仪原始数据获取 
        i2c.send(i2cid, i2cslaveaddr,MPU6050_RA_GYRO_XOUT_H)        --获取X方向陀螺仪的地址
        buffer = i2c.recv(i2cid, i2cslaveaddr, 2)
        _,gyro.x = pack.unpack(buffer,">h")
        i2c.send(i2cid, i2cslaveaddr,MPU6050_RA_GYRO_YOUT_H)        --获取Y方向陀螺仪的地址
        buffer = i2c.recv(i2cid, i2cslaveaddr, 2)
        _,gyro.y = pack.unpack(buffer,">h")
        i2c.send(i2cid, i2cslaveaddr,MPU6050_RA_GYRO_ZOUT_H)        --获取Z方向陀螺仪的地址
        buffer = i2c.recv(i2cid, i2cslaveaddr, 2)
        _,gyro.z = pack.unpack(buffer,">h")
        --计算陀螺仪的数据 单位：deg/10s
        gyro.x = gyro.x*100/MPU6050_GYRO_SEN
        gyro.y = gyro.y*100/MPU6050_GYRO_SEN
        gyro.z = gyro.z*100/MPU6050_GYRO_SEN
        log.info("6050gyro", "gyro.x",gyro.x,"gyro.y",gyro.y,"gyro.z",gyro.z)
        
        --位姿解算
        IMUupdate(gyro.x/10,gyro.y/10,gyro.z/10,accel.x,accel.y,accel.z)
        log.info("6050angle", "Q_ANGLE.X",Q_ANGLE.X,"Q_ANGLE.Y",Q_ANGLE.Y,"Q_ANGLE.Z",Q_ANGLE.Z)
    end
end)
--]]
-----------------------------I2C RTC实验(mp6050)----------------------------
--[[
local ds1307 = {}
local DS1307_ADDRESS            =   0x68   -- 1101000
local i2cslaveaddr              =   DS1307_ADDRESS --slave address

---DS1307所用地址
local REG_SEC				    =   0x00
local REG_MIN				    =   0x01
local REG_HOUR			        =   0x02
local REG_DAY				    =   0x03
local REG_WEEK			        =   0x04
local REG_MON				    =   0x05
local REG_YEAR			        =   0x06
local REG_WAVE  		        =   0x07

local function i2c_send(data)
    i2c.send(i2cid, i2cslaveaddr, data)
end
local function i2c_recv(data,num)
    i2c.send(i2cid, i2cslaveaddr, data)
    local revData = i2c.recv(i2cid, i2cslaveaddr, num)
    return revData
end

local function bcd_to_hex(data)
    local hex = bit.rshift(data,4)*10+bit.band(data,0x0f)
    return hex;
end

local function hex_to_bcd(data)
    local bcd = bit.lshift(math.floor(data/10),4)+data%10
    return bcd;
end


-- ds1307初始化
-- @api ds1307.init(i2c_id)
-- @number i2c_id i2c_id
-- @return bool   成功返回true
-- @usage
-- ds1307.init(0)

function ds1307.init(i2c_id)
    i2cid = i2c_id
    i2c_send({REG_SEC, 0x00})   -- 00h 地址的第7位为器件时钟允许位(CH)，由于在开始上电时内部RAM内容随机，所以在初始化时将CH位设零（时钟允许）是非常重要的。
    log.info("ds1307 init_ok")
    return true
end


-- 获取时间
-- @api ds1307.read_time()
-- @return table 时间表
-- @usage
-- local time = ds1307.read_time()
-- log.info("ds1307.read_time",time.tm_year,time.tm_mon,time.tm_mday,time.tm_hour,time.tm_min,time.tm_sec)

function ds1307.read_time()
    -- read time
    local time_data = {}
    local data = i2c_recv(REG_SEC,7)
    time_data.tm_year  = bcd_to_hex(data:byte(7)) + 2000
    time_data.tm_mon   = bcd_to_hex(bit.band(data:byte(6),0x7f)) - 1
    time_data.tm_mday  = bcd_to_hex(data:byte(5))
    time_data.tm_wday  = data:byte(4)
    time_data.tm_hour  = bcd_to_hex(data:byte(3))
    time_data.tm_min   = bcd_to_hex(data:byte(2))
    time_data.tm_sec   = bcd_to_hex(data:byte(1))
	return time_data
end


-- 设置时间
-- @api ds1307.set_time(time)
-- @table time 时间表
-- @usage
-- local set_time = {tm_year=2021,tm_mon=3,tm_mday=0,tm_wday=0,tm_hour=0,tm_min=0,tm_sec=0}
-- ds1307.set_time(set_time)

function ds1307.set_time(time)
    -- set time
    local data7 = hex_to_bcd(time.tm_year + 2000)
    local data6 = hex_to_bcd(time.tm_mon + 1)   --月和星期 函数输入0时为1（方便初始化）
    local data5 = hex_to_bcd(time.tm_mday)
    local data4 = hex_to_bcd(time.tm_wday)
    local data3 = hex_to_bcd(time.tm_hour)
    local data2 = hex_to_bcd(time.tm_min)
    local data1 = hex_to_bcd(time.tm_sec)
    i2c_send({REG_SEC, data1,data2,data3,data4,data5,data6,data7})
end

--return ds1307


i2cid = 0
i2c_speed = i2c.FAST
sys.taskInit(function()
    i2c.setup(i2cid,i2c_speed)
    local time
    -- ds1307.init(i2cid)--初始化,传入i2c_id
    -- local time = ds1307.read_time()
    -- log.info("ds1307.read_time",time.tm_year,time.tm_mon,time.tm_mday,time.tm_hour,time.tm_min,time.tm_sec)
    -- sys.wait(1000)
    -- local set_time = {tm_year=2022,tm_mon=5,tm_mday=13,tm_wday=5,tm_hour=10,tm_min=10,tm_sec=30}
    -- ds1307.set_time(set_time)
    -- log.info("ds1307.set_time",(set_time.tm_year).."/"..(set_time.tm_mon).."/"..(set_time.tm_mday),"星期"..(set_time.tm_wday),(set_time.tm_hour)..":"..(set_time.tm_min)..":"..(set_time.tm_sec))
    while 1 do
        sys.wait(1000)
        time = ds1307.read_time()
        log.info("ds1307_read_time",(time.tm_year).."/"..(time.tm_mon).."/"..(time.tm_mday),"星期"..(time.tm_wday),(time.tm_hour)..":"..(time.tm_min)..":"..(time.tm_sec))
    end
end)
--]]
-----------------------------LCD屏测试----------------------------------
--[[
--屏幕初始化
--SCL->PB2  SDA->PB5  RES->PB3
--DC->PB1  CS->PB4  BL->PB0

local spi_lcd = spi.deviceSetup(0, pin.PB04, 0, 0, 8, 2000000, spi.MSB, 1, 1)
log.info(
    "lcd.init",
    lcd.init(
        "st7789",
        {
            port = "device",
            pin_dc = pin.PB01,
            pin_pwr = pin.PB00,
            pin_rst = pin.PB03,
            direction = 0,
            w = 240,
            h = 320,
            xoffset = 0,
            yoffset = 0
        },
        spi_lcd
    )
)
sys.taskInit(function()
    local buff = zbuff.create({141,21,16},0x105F)  -- lcd颜色填充
    log.info("lcd", "size", lcd.getSize())   --获得屏幕尺寸
    while 1 do
        log.info("lcd.setColor", lcd.setColor(0xFFFF,0x0000)) -- lcd颜色设置（背景色，前景色）
        log.info("lcd.drawPoint", lcd.drawPoint(120,10,0x001F))    -- 画一个点（点x/y; 绘画颜色:可选参数,默认前景色）
        sys.wait(1000)
        log.info("lcd.drawLine", lcd.drawLine(20,20,220,20,0x010F))  --画线 (两点x/y; 绘画颜色:可选参数,默认前景色)
        sys.wait(1000)
        log.info("lcd.drawRectangle", lcd.drawRectangle(20,30,100,70,0xF800))  --画框（左上x/y; 右下x/y; 绘画颜色:可选参数,默认前景色）
        sys.wait(1000)
        log.info("lcd.drawCircle", lcd.drawCircle(180,50,20,0x0CE0))   --画圆（圆心x/y; 半径; 绘画颜色:可选参数,默认前景色）
        sys.wait(1000)
        log.info("lcd.draw", lcd.draw(50,80,190,100,buff))             --左上x/y; 右下x/y; 字符串或buff对象
        sys.wait(1000)
        log.info("lcd.fill",lcd.fill(50,110,190,130,0x0F0F))  -- lcd颜色填充（左上x/y; 右下x/y; 绘画颜色:可选参数,默认背景色）
        sys.wait(1000)
        log.info("lcd.setFont", lcd.setFont(lcd.font_opposansm16))  --设置为字体,对之后的drawStr有效,调用lcd.drawStr前一定要先设置; 使用中文字体需在luat_conf_bsp.h中开启相对应的宏
        log.info("lcd.drawStr", lcd.drawStr(90,155,"TEST"))     --左下角x/y; 内容; str颜色 注意:此参数可选，如不填写则使用之前设置的颜色，绘制只会绘制字体部分，背景需要自己清除。
        sys.wait(1000)
        log.info("lcd.drawQrcode",lcd.drawQrcode(110,170,"https://www.buaa.edu.cn/",1))  --缓冲区绘制QRCode(x/y坐标; 二维码内容; 显示大小：可选，不可小于21,默认21)
        sys.wait(1000)
        log.info("lcd.drawXbm",lcd.drawXbm(112, 200, 16,16, string.char(        -- 取模使用PCtoLCD2002软件即可 在(0,0)为左上角,绘制 16x16 "今" 的位图
            0x80,0x00,0x80,0x00,0x40,0x01,0x20,0x02,0x10,0x04,0x48,0x08,0x84,0x10,0x83,0x60,
            0x00,0x00,0xF8,0x0F,0x00,0x08,0x00,0x04,0x00,0x04,0x00,0x02,0x00,0x01,0x80,0x00
        )))
        sys.wait(1000)
        if lcd.showImage then
            log.info("lcd.showImage",lcd.showImage(0,0,"/luadb/logo.jpg"))   --显示图片 Air101不适用
            sys.wait(1000)
        end
        log.info("lcd.off",lcd.off())  -- 关闭lcd显示屏背光
        sys.wait(1000)
        log.info("lcd.on",lcd.on())   -- 开启lcd显示屏背光
        sys.wait(1000)
        log.info("lcd.invon",lcd.invoff())   --lcd反显关闭
        sys.wait(1000)
        log.info("lcd.invoff",lcd.invon())  --lcd反显开启(默认开启)
        sys.wait(1000)
        log.info("lcd.clear",lcd.clear())  -- lcd清屏（屏幕颜色:可选参数,默认背景色）
        sys.wait(1000)
    end
end)
--]]
-----------------------------LCD实验 2------------------------------
--[[
spi_lcd = spi.deviceSetup(0,pin.PB04,0,0,8,20*1000*1000,spi.MSB,1,1)

-- 此为合宙售卖的2.4寸TFT LCD 分辨率:240X320 屏幕ic:GC9306 购买地址:https://item.taobao.com/item.htm?spm=a1z10.5-c.w4002-24045920841.39.6c2275a1Pa8F9o&id=655959696358
-- lcd.init("gc9a01",{port = "device",pin_dc = pin.PB01, pin_pwr = pin.PB00, pin_rst = pin.PB03,direction = 0,w = 240,h = 320,xoffset = 0,yoffset = 0},spi_lcd)

-- 此为合宙售卖的1.8寸TFT LCD LCD 分辨率:128X160 屏幕ic:st7735 购买地址:https://item.taobao.com/item.htm?spm=a1z10.5-c.w4002-24045920841.19.6c2275a1Pa8F9o&id=560176729178
-- lcd.init("st7735",{port = "device",pin_dc = pin.PB01, pin_pwr = pin.PB00, pin_rst = pin.PB03,direction = 0,w = 128,h = 160,xoffset = 2,yoffset = 1},spi_lcd)

-- 此为合宙售卖的1.54寸TFT LCD LCD 分辨率:240X240 屏幕ic:st7789 购买地址:https://item.taobao.com/item.htm?spm=a1z10.5-c.w4002-24045920841.20.391445d5Ql4uJl&id=659456700222
-- lcd.init("st7789",{port = "device",pin_dc = pin.PB01, pin_pwr = pin.PB00, pin_rst = pin.PB03,direction = 0,w = 240,h = 240,xoffset = 0,yoffset = 0},spi_lcd)

-- 此为合宙售卖的0.96寸TFT LCD LCD 分辨率:160X80 屏幕ic:st7735s 购买地址:https://item.taobao.com/item.htm?spm=a1z10.5-c.w4002-24045920841.19.6c2275a1Pa8F9o&id=560176729178
lcd.init("st7735s",{port = "device",pin_dc = pin.PB01, pin_pwr = pin.PB00, pin_rst = pin.PB03,direction = 2,w = 160,h = 80,xoffset = 0,yoffset = 24},spi_lcd)

-- 此为合宙售卖的2.4寸TFT LCD 分辨率:240X320 屏幕ic:GC9306 购买地址:https://item.taobao.com/item.htm?spm=a1z10.5-c.w4002-24045920841.39.6c2275a1Pa8F9o&id=655959696358
--lcd.init("gc9306x",{port = "device",pin_dc = pin.PB01 ,pin_rst = pin.PB00, pin_pwr = pin.PB03,direction = 0,w = 240,h = 320,xoffset = 0,yoffset = 0},spi_lcd)

-- 不在上述内置驱动的, 看demo/lcd_custom

sys.taskInit(function()
    -- sys.wait(1000)
    -- API 文档 https://wiki.luatos.com/api/lcd.html
    lcd.invoff()
    local i
    if lcd.showImage then
        lcd.showImage(0,20,"/luadb/buaa.jpg")   --放不了太多照片
        sys.wait(2000)
        lcd.clear()
        -- lcd.showImage(0,80,"/luadb/logo.jpg")
        while true do
            for i=0,5 do
                lcd.showImage(48,8,"/luadb/i"..i..".jpg")
                sys.wait(100)
            end
        end
    else
        log.info("lcd.drawLine", lcd.drawLine(0,0,159,79,0x001F))
        log.info("lcd.drawRectangle", lcd.drawRectangle(20,40,120,70,0xF800))
        log.info("lcd.drawCircle", lcd.drawCircle(50,50,20,0x0CE0))
    end
end)
--]]
----------------------------视频播放实验-------------------------------
--[[
sys.taskInit(function()
    sdio.init(0)
    sdio.sd_mount(0,"/sd",0)

    spi_lcd = spi.deviceSetup(0,pin.PB04,0,0,8,20*1000*1000,spi.MSB,1,1)
    log.info("lcd.init",
    lcd.init("st7735s",{port = "device",pin_dc = pin.PB01, pin_pwr = pin.PB00,pin_rst = pin.PB03,direction = 2,w = 160,h = 80,xoffset = 0,yoffset = 24},spi_lcd))
    lcd.invoff()
    -- 使用ffmpeg.exe将视频转成字节流文件sxd.rgb放入TF卡
    -- 先缩放成目标大小
    -- ffmpeg -i sxd.mp4 -vf scale=160:80 sxd.avi
    -- 然后转rbg565ble 字节流
    -- ffmpeg -i sxd.avi -pix_fmt rgb565be -vcodec rawvideo sxd.rgb
    local rgb_file = "sxd.rgb"
    local file_size = fs.fsize("/sd/"..rgb_file)
    print("/sd/"..rgb_file.." file_size",file_size)
    local file = io.open("/sd/"..rgb_file, "rb")
    if file then
        local file_cnt = 0
        local buff = zbuff.create(25600)--分辨率160*80 160*80*2=25600
        repeat
            if file:fill(buff) then
                file_cnt = file_cnt + 25600
                lcd.draw(0, 0, 159, 79, buff)
                sys.wait(20)
            end
        until( file_size - file_cnt < 25600 )
        local temp_data = file:fill(buff,0,file_size - file_cnt)
        lcd.draw(0, 0, 159, 79, buff)
        sys.wait(30)
        file:close()
    end
    while true do
        sys.wait(1000)
    end
end)
--]]
-------------------------------LVGL实验---------------------------------
--[[ --屏幕初始化
local spi_lcd = spi.deviceSetup(0, pin.PB04, 0, 0, 8, 2000000, spi.MSB, 1, 1)
log.info(
    "lcd.init",
    lcd.init(
        "st7789",
        {
            port = "device",
            pin_dc = pin.PB01,
            pin_pwr = pin.PB00,
            pin_rst = pin.PB03,
            direction = 0,
            w = 240,
            h = 320,
            xoffset = 0,
            yoffset = 0
        },
        spi_lcd
    )
)

sys.taskInit(function()
    lcd.invoff()
    log.info("lvgl", lvgl.init())           --lvgl初始化
    lvgl.disp_set_bg_color(nil, 0xFFFFFF)   --设置页面背景色

    local scr = lvgl.obj_create(nil, nil)   --创建屏幕（画布）


    local btn = lvgl.btn_create(scr)        --创建按钮
    local btn1 = lvgl.btn_create(scr)       --创建按钮

    -- 位置、大小、布局：http://lvgl.100ask.net/8.2/overview/coords.html#simple-way
    -- C:\Users\YUE\Desktop\MyLuatOS\LuatOS\components\lvgl\binding
    -- C:\Users\YUE\Desktop\MyLuatOS\LuatOS\components\lvgl\src\lv_core\lv_obj.h

    -- 坐标位置
    --lvgl.obj_set_x(btn1, 10)
    --lvgl.obj_set_y(btn1, 20)
    lvgl.obj_set_pos(btn1, 20, 40)
    -- x 和 y 坐标是从父内容区域的左上角开始测量的

    -- 对齐（align）
    lvgl.obj_align(btn, scr, lvgl.ALIGN_CENTER, 0, -40)  --按键对齐到屏幕中间
    -- lvgl.scr_act():获取默认显示的活动屏幕，此处可代替scr
    -- 可取以下对齐选项 C:\Users\YUE\Desktop\MyLuatOS\LuatOS\components\lvgl\gen\luat_lv_enum.h
    -- ALIGN_*_TOP_LEFT  ALIGN_*_TOP_MID  ALIGN_TOP_*_RIGHT    
    -- ALIGN_*_LEFT_MID  ALIGN_CENTER  ALIGN_*_RIGHT_MID
    -- ALIGN_*_BOTTOM_LEFT  ALIGN_*_BOTTOM_MID ALIGN_*_BOTTOM_RIGHT
    -- * 为 IN 或 OUT
    -- 0, 0 表示x、y轴的偏移量均为0

    -- 大小（size）
    --lvgl.obj_set_width(btn1, 200)   
    --lvgl.obj_set_height(btn1, 100)
    lvgl.obj_set_size(btn1, 200, 50)   -- 宽度、高度像素大小

    -- 样式使用（style）
    -- 位置、大小和对齐属性是样式属性。
    -- 该部分还没移植好

    -- 创建标签
    local label = lvgl.label_create(btn)
    local label1 = lvgl.label_create(btn1)
    lvgl.label_set_text(label, "LuatOS!")  --按钮内容
    lvgl.label_set_text(label1, "BUAA & TJ")  --按钮内容



    --组件在：C:\Users\YUE\Desktop\MyLuatOS\LuatOS\components\lvgl\gen\lv_widgets 

    --创建进度条
    local bar = lvgl.bar_create(scr)
    lvgl.obj_set_size(bar, 200, 20)
    lvgl.obj_align(bar, scr, lvgl.ALIGN_IN_TOP_MID, 0, 10)  



    -- 回调函数
    slider_event_cb = function(obj, event)
        if event == lvgl.EVENT_VALUE_CHANGED then 
            local val = (lvgl.slider_get_value(obj) or "0").."%"
            lvgl.label_set_text(slider_label, val)
            lvgl.obj_align(slider_label, obj, lvgl.ALIGN_OUT_BOTTOM_MID, 0, 10)
        end
    end

    -- 创建滑动条
    slider = lvgl.slider_create(scr, nil)
    lvgl.obj_set_size(slider, 200, 20)
    lvgl.obj_align(slider, scr, lvgl.ALIGN_CENTER, 0, 0)
    lvgl.obj_set_event_cb(slider, slider_event_cb)

    -- 创建标签
    slider_label = lvgl.label_create(slider)
    lvgl.label_set_text(slider_label, "0%")
    lvgl.obj_align(slider_label, slider, lvgl.ALIGN_OUT_BOTTOM_MID, 0, 10)


    -- style = lvgl.style_create()
    -- lvgl.style_init(style)
    -- --lvgl.style_set_radius(style, lvgl.STATE_DEFAULT, 20)
    -- lvgl.style_set_bg_color(style, lvgl.STATE_DEFAULT, 0xff9e)

    -- --创建一个 Arc
    -- arc = lvgl.arc_create(scr)
    -- --设置 Arc 的大小
    -- lvgl.obj_set_size(arc, 150, 150)
    -- --设置Arc到 0 度位置的偏移量
    -- lvgl.arc_set_rotation(arc, 135)
    -- --设置Arc背景角度范围
    -- lvgl.arc_set_bg_angles(arc, 0, 270)
    -- --设置Arc设定值
    -- lvgl.arc_set_value(arc, 40)
    -- --设置 Arc 的位置
    -- lvgl.obj_align(arc, scr, lvgl.ALIGN_CENTER, 0, 100)
    -- lvgl.obj_add_style(arc, lvgl.ARC_PART_BG, style)



    set_angle = function(obj, value)
        lv_arc_set_value(obj, value)
    end

    arc1 = lvgl.arc_create(scr)
    lvgl.arc_set_rotation(arc1, 270)
    lvgl.arc_set_bg_angles(arc1, 0, 360)
    lvgl.obj_remove_style(arc1, lvgl.ARC_PART_KNOB)   --Be sure the knob is not displayed
    --lvgl.obj_clear_flag(arc1, lvgl.OBJ_FLAG_CLICKABLE)  --To not allow adjusting by click
    lvgl.obj_align(arc1, scr, lvgl.ALIGN_CENTER, 0, 100)
    
    anim = lvgl.anim_create()
    lvgl.anim_init(anim)
    lvgl.anim_set_var(anim, arc1)
    lvgl.anim_set_exec_cb(anim, set_angle)
    lvgl.anim_set_time(anim, 1000);
    lvgl.anim_set_repeat_count(anim, 100)   --Just for the demo
    lvgl.anim_set_repeat_delay(anim, 500);
    lvgl.anim_set_values(anim, 0, 100);
    lvgl.anim_start(anim);
    
    lvgl.scr_load(scr)
end) 
--]]
--------------------------tm1637数码管显示实验---------------------------
--实现原理：数码管每一秒变化计时一次，短按下boot按键开始计时，再次短按下结束计时，长按重新计时
--CLK <-> PA0   DIO <-> PA4
--[[
local tm1637 = require "tm1637"
local i1,i2,i3,i4
local ps = true
local cs = false   --计数状态
local rs = false   --重新计数状态

sys.taskInit(function()
    tm1637.init(1,4)
    while 1 do
        i1=0
        while i1<=5 do
            i2=0
            while i2<=9 do
                i3=0
                while i3<=5 do
                    i4=0
                    while i4 <= 9 do
                        if rs == true then
                            i1,i2,i3,i4=0,0,0,0
                            tm1637.singleShow(0,i1)
                            tm1637.singleShow(1,i2,true)
                            tm1637.singleShow(2,i3)
                            tm1637.singleShow(3,i4)
                            cs = false
                            rs = false
                        else
                            if cs == true then
                                tm1637.singleShow(0,i1)
                                tm1637.singleShow(1,i2,ps)
                                tm1637.singleShow(2,i3)
                                tm1637.singleShow(3,i4)
                                sys.wait(382)
                                ps = not ps
                                tm1637.singleShow(1,i2,ps)
                                sys.wait(618)
                                ps = not ps
                                i4 = i4 + 1
                            else
                                sys.wait(10)
                            end    
                        end
                    end
                    i3=i3+1
                end
                i2=i2+1
            end
            i1=i1+1
        end
    end
end)

local button_timer_outtime = 10 --按键定时器: 10ms
local button_shake_time = 1     --按键消抖时间: button_shake_time*button_timer_outtime
local button_long_time = 100    --按键长按时间: button_shake_time*button_timer_outtime
local button_state = 0 --按键状态（松开0，按下1）
local button_cont = 0

button = gpio.setup(pin.PA00,	--button获取电平的闭包，button()表示状态
                    function() end,						
                    gpio.PULLUP,gpio.FALLING)  --中断模式 --按键按下接地，因此需要上拉

sys.taskInit(function()
    while 1 do
		if button() == 0 then   --按键当前时刻为低电平（被按下） 此处根据上下拉选择0/1
			button_cont = button_cont + 1   --计数值加一
            if button_cont > button_long_time then  --计数次数大于长按阈值
				rs = true
			end
		else --按键当前时刻电平与状态不符
			if button_cont < button_shake_time then  --计数次数小于抖动阈值（视为抖动）
			else
				if button_cont < button_long_time then  --计数次数小于长按阈值
					cs = not cs     --正常按键情况
				else
                    rs = true       --长按情况
				end
			end
			button_cont = 0   --计数清零
			button_state = 0  --松开状态
		end
		sys.wait(button_timer_outtime)
    end
end)
--]]
--------------------------tm1637数码管显示实验（V2.0）-------------------
--[[
--实现原理：数码管每一秒变化计时一次，短按下boot按键开始计时，再次短按下结束计时，长按重新计时,连按两下熄屏
local tm1637 = require "tm1637"
local i1,i2,i3,i4
local ps = true
local cs = false   --计数状态
local rs = false   --重新计数状态
local ss = true    --显示状态

sys.taskInit(function()
    tm1637.init(1,4)
    while 1 do
        i1=0
        while i1<=5 do
            i2=0
            while i2<=9 do
                i3=0
                while i3<=5 do
                    i4=0
                    while i4 <= 9 do
                        if ss == true then
                            if cs == true then
                                i1,i2,i3,i4=0,0,0,0
                                tm1637.singleShow(0,i1)
                                tm1637.singleShow(1,i2,true)
                                tm1637.singleShow(2,i3)
                                tm1637.singleShow(3,i4)
                                cs = false
                                rs = false
                                ss = false
                            else
                                tm1637.clear()
                                sys.wait(10)
                            end
                        elseif rs == true then
                            i1,i2,i3,i4=0,0,0,0
                            tm1637.singleShow(0,i1)
                            tm1637.singleShow(1,i2,true)
                            tm1637.singleShow(2,i3)
                            tm1637.singleShow(3,i4)
                            cs = false
                            rs = false
                        else
                            if cs == true then
                                tm1637.singleShow(0,i1)
                                tm1637.singleShow(1,i2,ps)
                                tm1637.singleShow(2,i3)
                                tm1637.singleShow(3,i4)
                                sys.wait(382)
                                ps = not ps
                                tm1637.singleShow(1,i2,ps)
                                sys.wait(618)
                                ps = not ps
                                i4 = i4 + 1
                            else
                                sys.wait(10)
                            end    
                        end
                    end
                    i3=i3+1
                end
                i2=i2+1
            end
            i1=i1+1
        end
    end
end)

local button_timer_outtime = 10 --按键定时器: 10ms
local button_shake_time = 1     --按键消抖时间: button_shake_time*button_timer_outtime
local button_long_time = 100    --按键长按时间: button_shake_time*button_timer_outtime
local button_state = 0          --按键状态（松开0，按下1）
local button_cont = 0

local count_state = false        --s是否处在两次按下间计数状态
local button_cont_release = 0    
local button_release_time = 20    --最长连按时间: button_release_time*button_timer_outtime

button = gpio.setup(pin.PA00,	--button获取电平的闭包，button()表示状态
function() end,						--中断模式
gpio.PULLUP,gpio.FALLING)  --按键按下接地，因此需要上拉

sys.taskInit(function()
    while 1 do
		if button() == 0 then   --按键当前时刻为低电平（被按下） 此处根据上下拉选择0/1
			button_cont = button_cont + 1   --计数值加一
			if button_cont > button_long_time then  --计数次数大于长按阈值
				rs = true
			end
		else --按键当前时刻电平与状态不符
			if button_cont < button_shake_time then  --计数次数小于抖动阈值（视为抖动）
                if button_cont_release > button_release_time then
                    button_cont_release = 0
                    count_state = false
                    cs = not cs     --正常按键情况
                end
			else
				if button_cont < button_long_time then  --计数次数小于长按阈值
                    if button_cont_release <= button_release_time and count_state == true then
                        button_cont_release = 0
                        ss = true     --按键连按情况
                        cs = false
                    end
                    count_state = not count_state
				else
					rs = true  --长按情况
				end
			end
			button_cont = 0   --计数清零
			button_state = 0  --松开状态
            if button_cont_release <= button_release_time and count_state == true then
                button_cont_release = button_cont_release + 1   --松开计算值加一
            end
		end
		sys.wait(button_timer_outtime)
    end
end)
--]]
----------------------------ADKeyboard实验------------------------------
--[[
--接线：PB00 <-> VCC    PA01 <-> OUT
--PB00 -> PWM0  PA01 -> ADC0  因为ADC最大运行电压为2.4V，所以这里用PWM输出
--PWM0 10000Hz 占空比为66%时  未按下2125(2.125)V
--K1~K5 0 270 655 1024 1522
local adc_key_val = {150, 500, 850, 1300, 2000}
local NUM_KEYS = 5
local adc_key_in
local key=0
local oldkey=0

local function get_key(input)
    local k = 1
    while k <= NUM_KEYS do               --lua用while比for好点
        if input<adc_key_val[k] then
            return k
        end
        k=k+1
    end
    if k>NUM_KEYS then
        k=0
    end
    return k
end

sys.taskInit(function()
    pwm.open(0, 10000, 66) -- 频率10000hz, 占空比66
    adc.open(0)  --PA1
    sys.wait(100)
    while 1 do
        _,adc_key_in = adc.read(0)
        --log.info("adc", adc_key_in)
        key=get_key(adc_key_in)
        if key ~= oldkey then
            oldkey=key
            if key>=1 then
                if key == 1 then
                    log.info("S1 OK")
                elseif key == 2 then
                    log.info("S2 OK")
                elseif key == 3 then
                    log.info("S3 OK")
                elseif key == 4 then
                    log.info("S4 OK")
                elseif key == 5 then
                    log.info("S5 OK")
                end
            end
        end
        sys.wait(100)
    end
end)
--]]
------------------------------RTC实验-----------------------------------
--[[
--RTC计时及唤醒功能
local t
sys.taskInit(function()
    rtc.set({year=2022,mon=5,day=6,hour=11,min=6,sec=0})
    rtc.timerStart(0, {year=2022,mon=5,day=6,hour=11,min=6,sec=10})         --设置唤醒时间（只能LIGHT模式，DEEP和HIB模式不行）
    t = rtc.get()
    log.info("Set time: "..t.year.."/"..t.mon.."/"..t.day,t.hour..":"..t.min..":"..t.sec)
    sys.wait(100)                --延不延时都可以
    log.info("Light Sleep")
    pm.request(pm.LIGHT)
    log.info("Wake Up")
    t = rtc.get()
    log.info("Wakeup time: "..t.year.."/"..t.mon.."/"..t.day,t.hour..":"..t.min..":"..t.sec)
    sys.wait(100)
    while 1 do
        t = rtc.get()
        --log.info("rtc", json.encode(t))
        log.info(t.year.."/"..t.mon.."/"..t.day,t.hour..":"..t.min..":"..t.sec)
        sys.wait(1000)       --wait的时长和rtc时间不一样，所以显示不一定每次都不同（实测调试时rtc走的慢挺多）
    end
end)
--]]
--[[
--官方demo案例
sys.taskInit(function()
    log.info("os.date()", os.date())
    local t = rtc.get()
    log.info("rtc", json.encode(t))
    sys.wait(2000)
    rtc.set({year=2021,mon=8,day=31,hour=17,min=8,sec=43})
    log.info("os.date()", os.date())
    -- rtc.timerStart(0, {year=2021,mon=9,day=1,hour=17,min=8,sec=43})
    -- rtc.timerStop(0)
    while 1 do
        log.info("os.date()", os.date())
        local t = rtc.get()
        log.info("rtc", json.encode(t))
        sys.wait(1000)
    end
end)
--]]
------------------ 失败：单总线实验之dht11（温湿度传感器）---------------
--失败原因：不支持微妙级延时
--[[
local function DHT11_reset(pin)
    gpio.setup(pin,0)
    gpio.set(pin,0)
    sys.wait(20)
    gpio.set(pin, 1) -- gpio设置为低电平
    local sm =  statem.create()
            :usleep(30)     -- 休眠10us
            :finish()
    sm:exec()
end

local function DHT11_check(pin)
    local retry=0
    gpio.setup(pin,nil)
    while(gpio.get(pin) and retry<100) do
        retry=retry+1
        local sm = statem.create()
        :usleep(1)     -- 休眠1us
        :finish()
        sm:exec()
    end
    if(retry>=100) then
        return 1
    else
        retry=0
    end
    while ((not gpio.get(pin)) and retry<100) do
        retry=retry+1
        local sm1 = statem.create()
        :usleep(1)     -- 休眠1us
        :finish()
        sm1:exec()
    end
    if retry>=100 then
        return 1
    end
    return 0
end

sys.taskInit(function()
    local pin=pin.PB05
    DHT11_reset(pin)          --复位DHT11
    log.info("check",DHT11_check(pin))     --等待DHT11的回应
end)
--]]
------------------sensor库：ds18b20温湿度传感器实验----------------------
--[[
--sensor库函数：https://wiki.luatos.com/api/sensor.html
sys.taskInit(function()
    while 1 do
        sys.wait(5000)          --每五秒测量一次
        local val,result = sensor.ds18b20(pin.PB05, true) -- PB05为数据引脚且校验CRC值
        -- val 301 == 30.1摄氏度
        -- result true 读取成功
        log.info("ds18b20", (val/10).."°C", result)
    end
end)
--]]
----------------------错误：sensor库：ws2812b彩灯实验--------------------
----------------问题：101/103不支持这个接口，gpio的速度不够---------------
--[[
--sensor库函数：https://wiki.luatos.com/api/sensor.html
-- vcc -> 5v
--12MHz -> 1nop -> 1us   40MHz -> 1nop -> 0.3us
--T0H -> 0.4±0.15us
--T0L -> 0.85±0.15us
--T1H -> 0.85±0.15us
--T1L -> 0.4±0.15us

local buff = zbuff.create({8,8,24})
buff:drawLine(1,2,5,6,0x000000)
sensor.ws2812b(pin.PA01,buff,300,700,700,700)
--]]
------------------sensor库：HX711模块/称重传感器实验----------------------
--sensor库函数：https://wiki.luatos.com/api/sensor.html

--------------------------u8g2 oled 库实验------------------------------
--[[
-- 初始化显示屏
log.info("init ssd1306")
-- 初始化硬件spi的ssd1306   PB2接CLK（D0），PB5接MOSI（D1）  --不知道为啥spi没成功
--u8g2.begin({ic = "ssd1306",direction = 0,mode="spi_hw_4pin",spi_id=0,spi_res=pin.PB03,spi_dc=pin.PB01,spi_cs=pin.PB04}) -- direction 可选0 90 180 270

-- 初始化硬件i2c的ssd1306    接线：D0->PA1 D1->PA4 RES->reset (DC->GND CS->GND 可不接）
u8g2.begin({ic = "ssd1306",direction = 0,mode="i2c_hw",i2c_id=0,i2c_speed = i2c.FAST}) -- direction 可选0 90 180 270
-- 初始化软件i2c的ssd1306
-- u8g2.begin({ic = "ssd1306",direction = 0,mode="i2c_sw", i2c_scl=1, i2c_sda=4}) -- 通过PA1 SCL / PA4 SDA模拟
u8g2.SetFontMode(1)
u8g2.ClearBuffer()
u8g2.SetFont(u8g2.font_opposansm8)
u8g2.DrawUTF8("U8g2+LuatOS", 32, 22)
u8g2.SetFont(u8g2.font_opposansm12_chinese)
u8g2.DrawUTF8("中文测试", 40, 38)
u8g2.SendBuffer()
-- 联网主流程
sys.taskInit(function()
    while true do
        sys.wait(2000)
        u8g2.ClearBuffer()
        u8g2.SetFont(u8g2.font_opposansm12_chinese)
        u8g2.DrawUTF8("屏幕宽度", 20, 24)
        u8g2.DrawUTF8("屏幕高度", 20, 42)
        u8g2.SetFont(u8g2.font_opposansm8)
        u8g2.DrawUTF8(":"..u8g2.GetDisplayWidth(), 84, 24)
        u8g2.DrawUTF8(":"..u8g2.GetDisplayHeight(), 84, 42)
        u8g2.SendBuffer()

        sys.wait(2000)
        u8g2.ClearBuffer()
        u8g2.SetFont(u8g2.font_opposansm12_chinese)
        u8g2.DrawUTF8("画线测试：", 30, 24)
        for i = 0, 128, 8 do
            u8g2.DrawLine(0,40,i,40)
            u8g2.DrawLine(0,60,i,60)
            u8g2.SendBuffer()
        end

        sys.wait(1000)
        u8g2.ClearBuffer()
        u8g2.SetFont(u8g2.font_opposansm12_chinese)
        u8g2.DrawUTF8("画圆测试：", 30, 24)
        u8g2.DrawCircle(30,50,10,15)
        u8g2.DrawDisc(90,50,10,15)
        u8g2.SendBuffer()

        sys.wait(1000)
        u8g2.ClearBuffer()
        u8g2.SetFont(u8g2.font_opposansm12_chinese)
        u8g2.DrawUTF8("椭圆测试：", 30, 24)
        u8g2.DrawEllipse(30,50,6,10,15)
        u8g2.DrawFilledEllipse(90,50,6,10,15)
        u8g2.SendBuffer()

        sys.wait(1000)
        u8g2.ClearBuffer()
        u8g2.SetFont(u8g2.font_opposansm12_chinese)
        u8g2.DrawUTF8("方框测试：", 30, 24)
        u8g2.DrawBox(30,40,30,24)
        u8g2.DrawFrame(90,40,30,24)
        u8g2.SendBuffer()

        sys.wait(1000)
        u8g2.ClearBuffer()
        u8g2.SetFont(u8g2.font_opposansm12_chinese)
        u8g2.DrawUTF8("圆角方框：", 30, 24)
        u8g2.DrawRBox(30,40,30,24,8)
        u8g2.DrawRFrame(90,40,30,24,8)
        u8g2.SendBuffer()

        sys.wait(1000)
        u8g2.ClearBuffer()
        u8g2.SetFont(u8g2.font_opposansm12_chinese)
        u8g2.DrawUTF8("符号测试：", 30, 24)
        u8g2.DrawUTF8("显示雪人", 30, 38)
        u8g2.SetFont(u8g2.font_unifont_t_symbols)
        u8g2.DrawGlyph( 50, 60, 0x2603 )
        u8g2.SendBuffer()

        sys.wait(1000)
        u8g2.ClearBuffer()
        u8g2.SetFont(u8g2.font_opposansm12_chinese)
        u8g2.DrawUTF8("三角测试：", 30, 24)
        u8g2.DrawTriangle(30,60, 60,30, 90,60)
        u8g2.SendBuffer()

        sys.wait(1000)
        u8g2.ClearBuffer()
        u8g2.SetFont(u8g2.font_opposansm12_chinese)
        u8g2.DrawUTF8("绘图测试：", 0, 16)
        -- 取模使用PCtoLCD2002软件即可
        -- 在(0,0)为左上角,绘制 24x24 "越" 的位图
        u8g2.DrawXBM(56, 24, 24, 24, string.char(
            0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x81,0x01,0x00,0x83,0x01,0x00,0x81,0x1D,0x00,0x01,0x19,0x00,0x07,0x01,0xC0,0x01,0x05,0x00,0x01,0x03,0x80,0xE7,
            0x09,0x78,0x21,0x1A,0x0C,0x21,0x0A,0x20,0x2F,0x06,0x30,0x61,0x06,0x30,0x21,0x2D,0xD8,0xA1,0x38,0x08,0x03,0x30,0x04,0x0C,0x00,0x00,0x38,0x00,0x00,0xE0,0x03,0x00,
            0x80,0x7F,0x00,0x00,0x00,0x00,0x00,0x00))
        u8g2.SendBuffer()

        -- u8g2.close()
    end
end)
--]]
------------------------------SD card 实验（SDIO）----------------------
--[[
-- 特别提醒, 由于FAT32是DOS时代的产物, 文件名超过8个字节是需要额外支持的(需要更大的ROM)
-- 例如 /sd/boottime 是合法文件名, 而/sd/boot_time就不是合法文件名, 需要启用长文件名支持.
--PB_06 -> GPIO_22 / UART1_TX / SDIO_CLK
--PB_07 -> GPIO_23 / UART1_RX / SDIO_CMD
--PB_08 -> GPIO_24 / SDIO_D0
--PB_09 -> GPIO_25 / SDIO_D1
--PB_10 -> GPIO_26 / SDIO_D2
--PB_11 -> GPIO_27 / SDIO_D3

--sdio:https://wiki.luatos.com/api/sdio.html
--io:https://wiki.luatos.com/api/io.html

local function fatfs_test()
    log.info("sdio", "call sdio.init")          -- sdio 初始化
    sdio.init(0)
    log.info("sdio", "call sdio.sd_mount")      -- 挂载SD卡, 使用FATFS文件系统(sdio总线id 挂载路径,默认”/sd”,不允许以”/”结尾 是否自动格式化,默认是true)
    sdio.sd_mount(0, "/sd")                     -- 挂载完成后就可以使用io及f:操作
    local f = io.open("/sd/boottime", "rb")     -- rb:以二进制的方式打开一个文件用于只读(无该文件返回0)
    local c = 0
    if f then                                   --存在文件则读该文件（第一次使用没有该文件，不会进入if内程序）
        local data = f:read("*a")
        log.info("fs", "data", data, data:toHex())
        c = tonumber(data)
        f:close()
    end
    log.info("fs", "boot count", c)             -- 第一次c=0
    c = c + 1
    f = io.open("/sd/boottime", "wb")           -- wb:以二进制的方式打开一个文件用于只写(无该文件返回0)
    if f ~= nil then
        log.info("fs", "write c to file", c, tostring(c))      --把c的值写入该文件中
        f:write(tostring(c))
        f:close()                               -- 记得f:close关闭文件
    else
        log.warn("sdio", "mount not good?!")    -- 无法执行io.open返回值则可能文件系统没有挂载成功
    end
    if fs then                                  -- 判断固件是否支持fs库（air101支持）
        log.info("fsstat", fs.fsstat("/"))      -- 打印根分区的信息
        log.info("fsstat", fs.fsstat("/sd"))
    end

    -- 测试一下追加, fix in 2021.12.21
    os.remove("/sd/test_a")
    sys.wait(50)
    f = io.open("/sd/test_a", "w")             -- 写打开，不存在创建，覆盖原文件
    if f then
        f:write("ABC")
        f:close()
    end
    f = io.open("/sd/test_a", "a+")             -- 读写打开，不存在创建，追加内容
    if f then
        f:write("def")
        f:close()
    end
    f = io.open("/sd/test_a", "r")
    if  f then
        local data = f:read("*a")
        log.info("data", data, data == "ABCdef")
        f:close()
    end

    -- 测试一下按行读取, fix in 2022-01-16
    f = io.open("/sd/testline", "w")     --写
    if f then
        f:write("abc\n")
        f:write("123\n")
        f:write("wendal\n")
        f:close()
    end
    sys.wait(100)
    f = io.open("/sd/testline", "r")     --读
    if f then
        log.info("sdio", "line1", f:read("*l"))
        log.info("sdio", "line2", f:read("*l"))
        log.info("sdio", "line3", f:read("*l"))
        f:close()
    end
end


sys.taskInit(function()
    fatfs_test() -- 每次开机,把记录的数值+1
    local f = io.open("/sd/mytest.txt", "r")
    if f then
        log.info("sdio", "mytest.txt", f:read("*a"))   -- https://wiki.luatos.com/luaGuide/luaReference.html 后面
        f:close()
    end
    while 1 do
        sys.wait(500)
    end
end)
--]]
----------------------------SPI flash实验（W25Q128）--------------------
--[[
--PB2接CLK，PB5接MOSI（DI），PB3接MISO（DO），PB4接CS
sys.taskInit(
    function()
        local spi_device = spi.deviceSetup(0,pin.PB04,0,0,8,2000000,spi.MSB,1,1)
        while 1 do
            local recv = spi_device:transfer(string.char(0x9f),nil,3)  --读flash的型号的通用命令0x9F，正常的话W25Q128应该会返回0xEF4018
            log.info("spi data",recv:toHex())
            sys.wait(1000)
        end
    end
)
--]]
-----------------基于SFUD 的 SPI flash实验（W25Q128）--------------------
--[[
sys.taskInit(function()
    -- log.info("sfud.init",sfud.init(0,20,20 * 1000 * 1000))--此方法spi总线无法挂载多设备
    -- sfud创建一个spi的硬件设备对象，然后通过sfud的抽象接口操作spi实现对flash的操作
    local spi_flash = spi.deviceSetup(0,20,0,0,8,20*1000*1000,spi.MSB,1,0)--PB6
    log.info("sfud.init",sfud.init(spi_flash))
    log.info("sfud.getDeviceNum",sfud.getDeviceNum())
    local sfud_device = sfud.getDeviceTable()
    log.info("sfud.write",sfud.write(sfud_device,1024,"sfud"))
    log.info("sfud.read",sfud.read(sfud_device,1024,4))

    -- 通过sfud.mount()这个函数，可以直接挂载到文件系统
    -- 把flash变成文件系统的一部分直接通过lua原生接口读写
    log.info("sfud.mount",sfud.mount(sfud_device,"/sfud"))
    log.info("fsstat", fs.fsstat("/sfud"))
    -- 挂载到文件系统后，新建一个文件写入1000字节数据，然后读一下文件大小，再把文件内容读出来。
    local f = io.open ("/sfud/a.txt", "wb")
    f:write(string.rep("1234567890", 100))
    f:close()
    log.info("fsize", fs.fsize("/sfud/a.txt"))
    f = io.open ("/sfud/a.txt", "rb")
    local data = f:read("*a")
    log.info("fs", "data", data)
    while 1 do
        sys.wait(1000)
    end
end)
--]]
------------------------------fs 文件系统操作实验-----------------------------------
--[[
local function fs_test()
    local f = io.open("/boot_time", "rb")       -- 读写一个文件 boot_time
    local c = 0
    if f then
        local data = f:read("*a")
        log.info("fs", "data", data, data:toHex())
        c = tonumber(data)
        f:close()
    end
    log.info("fs", "boot count", c)
    c = c + 1
    f = io.open("/boot_time", "wb")
    --if f ~= nil then
    log.info("fs", "write c to file", c, tostring(c))
    f:write(tostring(c))
    f:close()
    --end

    if fs then
        log.info("fsstat", fs.fsstat("/"))   -- 打印根分区的信息
    end

    -- 读取刷机时加入的文件, 并演示按行读取 在luatide_ project.json或烧录时可选加入文件
    -- 刷机时选取的非lua文件, 均存放在/luadb/目录下, 单层无子文件夹
    f = io.open("/luadb/abc.txt", "a")
    if f then
        while true do
            local line = f:read("l")         --依次读取一行
            if not line or #line == 0 then
                break
            end
            log.info("fs", "read line", line)
        end
        f:close()
        log.info("fs", "close f")
    else
        log.info("fs", "pls add abc.txt!!")
    end
end

fs_test() -- 每次开机,把记录的数值+1

sys.taskInit(function()
    while 1 do
        sys.wait(500)
    end
end)
--]]
----------------------电源管理/低功耗实验-------------------------------
--[[
-- 感觉轻休眠比较常用
-- pm库：https://wiki.luatos.com/api/pm.html
local LEDA = gpio.setup(24, 0, gpio.PULLUP) -- PB8输出模式
local LEDB = gpio.setup(25, 0, gpio.PULLUP) -- PB9输出模式
local LEDC = gpio.setup(26, 0, gpio.PULLUP) -- PB10输出模式

-- 这里启动的task也是闪灯, 用于展示休眠状态
sys.taskInit(function()
    local count = 0
    local uid = mcu.unique_id() or ""
    while 1 do
        -- 一闪一闪亮晶晶
        LEDA(count & 0x01 == 0x01 and 1 or 0)
        LEDB(count & 0x02 == 0x02 and 1 or 0)
        LEDC(count & 0x03 == 0x03 and 1 or 0)
        log.info("gpio", "Go Go Go", uid:toHex(), count)
        sys.wait(500)
        count = count + 1
    end
end)

sys.taskInit(function()
    -- 对于air101/air103来说, request之后马上就会休眠
    -- 如果没有休眠, 请检查wakeup脚的电平
    -- 休眠分2种情况, RAM不掉电的LIGHT 和 RAM不掉电的HIB

    -- 这里的sys.wait是为了开机后不会马上休眠
    -- 实际情况是跑业务代码, 条件符合后休眠,这里是sys.wait只是演示业务执行
    sys.wait(5000)

    -- 先演示LIGHT模式休眠, RAM不掉电, IO保持, 唤醒后代码继续跑
    -- 设置休眠时长
    log.info("pm", "轻休眠5秒", "要么rtc唤醒,要么wakeup唤醒", "RAM不掉电")
    -- 启动底层定时器,在休眠模式下依然生效. 只触发一次
    pm.dtimerStart(0, 5000) -- 5秒后唤醒
    pm.request(pm.LIGHT)     -- 进入轻休眠, CPU停止, RAM保持, 外设保持, 可中断唤醒. 部分型号支持从休眠处继续运行

    -- 后面的语句, 正常情况下会在15000ms后执行，若唤醒则直接跑
    log.info("pm", "轻睡眠唤醒", "代码继续跑")
    -- 这里等待5000ms,模拟业务执行,期间gpio的闪灯task将继续运行
    sys.wait(5000)

    -- 接着演示 DEEP模式休眠, RAM掉电, IO失效, 唤醒相当于复位重启
    -- 因为已经唤醒过,dtimer已经失效, 重新设置一个
    log.info("pm", "深休眠5秒", "唤醒相当于复位重启", "RAM掉电")
    pm.dtimerStart(0, 5000)   -- 无法唤醒
    pm.request(pm.DEEP)       -- 进入深休眠, CPU停止, RAM掉电, 仅特殊引脚保持的休眠前的电平, 大部分管脚不能唤醒设备.
    -- 后面代码都不会执行, 如果执行了, 那100%是wakeup电平有问题
    log.info("pm", "这里的代码不会被执行")
end)
--]]
-----------------------(未测试)红外遥控模块实验-------------------------
--[[
-- ir库：https://wiki.luatos.com/api/ir.html
sys.taskInit(function()
    while true do
        --直接发
        ir.sendNEC(0, 0x11, 0x22)
        --外接了38K的PWM载波，只控制电平
        ir.sendNEC(0, 0x11, 0x22,0,true)
        sys.wait(1000)
    end
end)
--]]
















-----------------------------以下为不常用------------------------------
---------------------------内存信息获取实验----------------------------
--[[
sys.taskInit(function()
    local count = 1
    while 1 do
        sys.wait(1000)
        log.info("luatos", "hi", count, os.date())
        log.info("luatos", rtos.meminfo())       -- 获取内存信息 “sys”系统内存, “lua”虚拟机内存, 默认为lua虚拟机内存
        log.info("luatos", rtos.meminfo("sys"))  -- 返回：总内存大小 当前使用的内存大小 最大使用的内存大小 （单位字节）
        count = count + 1
    end
end)
--]]
--------------------CoreMark CPU测试实验（通常用不到）------------------
--[[
sys.taskInit(function()
    sys.wait(2000)
    if coremark then    -- 需固件支持
        if mcu and (rtos.bsp() == "air101" or rtos.bsp() == "air103") then
            mcu.setClk(240)
        end
        log.info("coremark", "G0-----------------------------")
        coremark.run()
        log.info("coremark", "Done---------------------------")
    else
        log.info("coremark", "need coremark lib")
    end
end)
--]]
---------------------------json生成和解析实验--------------------------
--[[
log.info("main", "json demo")
print(json.null)

local t = {
    a = 1,
    b = "abc",
    c = {
        1,2,3,4
    },
    d = {
        x = false,
        j = 111111
    },
    aaaa = 6666,
}

local s = json.encode(t)  --将对象序列化为json字符串

local st = json.decode(s)  --将字符串反序列化为对象

print(s)
print(st.a,st.b,st.d.x)
--]]
--------------------------crypto库：加/解密-------------------------------
--[[
-- crypto库：https://wiki.luatos.com/api/crypto.html
sys.taskInit(function()
    sys.wait(1000)

    -- 以下仅提供加密函数，为提供解密函数
    -- MD5加密算法,输出结果已经hex编码
    log.info("md5", crypto.md5("abc"))                               -- 计算字符串"abc"的md5
    log.info("hmac_md5", crypto.hmac_md5("abc", "1234567890"))       -- 计算字符串"abc"的hmac_md5，以"1234567890"为密钥

    -- SHA1加密算法,输出结果已经hex编码
    log.info("sha1", crypto.sha1("abc"))                             -- 计算字符串"abc"的sha1
    log.info("hmac_sha1", crypto.hmac_sha1("abc", "1234567890"))     -- 计算字符串"abc"的hmac_sha1，以"1234567890"为密钥

    -- SHA256加密算法,输出结果已经hex编码
    log.info("sha256", crypto.sha256("abc"))                         -- 计算字符串"abc"的sha256
    log.info("hmac_sha256", crypto.hmac_sha256("abc", "1234567890")) -- 计算字符串"abc"的hmac_sha256，以"1234567890"为密钥

    -- SHA512加密算法,输出结果已经hex编码
    log.info("sha512", crypto.sha512("abc"))                         -- 计算字符串"abc"的sha512
    log.info("hmac_sha512", crypto.hmac_sha512("abc", "1234567890")) -- 计算字符串"abc"的hmac_sha512，以"1234567890"为密钥

    -- AES加密, 未经Hex编码. AES-128-ECB 算法,待加密字符串如果超过32字节会报错,待查. by wendal 20200812
    -- 对称加密 算法名称，对齐方式，需要加密数据，密钥，IV值
    local data_encrypt = crypto.cipher_encrypt("AES-128-ECB", "PKCS7", "12345678901234 > 123456", "1234567890123456") --AES-128-ECB 算法
    local data2_encrypt = crypto.cipher_encrypt("AES-128-CBC", "PKCS7", "12345678901234 > 123456", "1234567890123456", "1234567890666666") --AES-128-CBC 算法
    log.info("AES", "aes-128-ecb", data_encrypt:toHex())             --进行Hex编码
    log.info("AES", "aes-128-cbc", data2_encrypt:toHex())

    -- AES解密, 未经Hex编码
    -- 对称解密 算法名称，对齐方式，需要解密的数据，密钥，IV值
    local data_decrypt = crypto.cipher_decrypt("AES-128-ECB", "PKCS7", data_encrypt, "1234567890123456")
    local data2_decrypt = crypto.cipher_decrypt("AES-128-CBC", "PKCS7", data2_encrypt, "1234567890123456", "1234567890666666")
    log.info("AES", "aes-128-ecb", data_decrypt)
    log.info("AES", "aes-128-cbc", data2_decrypt)
    log.info("mem", rtos.meminfo("sys"))

    -- 计算CRC16
    -- local crc = crypto.crc16("")

    -- 计算CRC16 modbus
    -- local crc = crypto.crc16_modbus(data)

    -- 计算CRC32
    -- local crc = crypto.crc32(data)

    -- 计算CRC8
    -- local crc = crypto.crc8(data)
    -- local crc = crypto.crc8(data, 0x31, 0xff, false)

    --totp的密钥  网站提供的密钥（就是BASE32编码后的结果）
    local secret = "VK54ZXPO74ISEM2E"
    --写死时间戳用来测试  可选，时间戳，默认当前时间
    local ts = 1646796576
    --生成十分钟的动态码验证下
    for i=1,600,30 do
        local r = crypto.totp(secret,ts+i)  -- 计算TOTP动态密码的结果（TOTP算法是一种从共享密钥和当前时间计算一次性密码的算法。）
        local time = os.date("*t",ts+i + 8*3600)--东八区  os.date格式化该时间
        log.info("totp",r,time.hour..":"..time.min..":"..time.sec)
    end
end)
--]]
------------------------------水墨屏实验（未验证）----------------------------

-- elink库：https://wiki.luatos.com/api/eink.html
-- 显示屏为佳显 1.54寸v2,200x200,快刷屏
-- 硬件接线
-- 显示屏SPI          --> Air101 SPI0

-- SPI0
-- SPI0_SCK               (PB2)
-- SPI0_MISO              (PB3)
-- SPI0_MOSI              (PB5)

-- 显示屏 Pin_BUSY        (PB1)
-- 显示屏 Pin_RES         (PA1)
-- 显示屏 Pin_DC          (PA4)
-- 显示屏 Pin_CS          (PB4)

function eink154_update()

    eink.clear()

    eink.print(16, 16, os.date(), 0, eink.font_opposansm12)
    eink.print(16, 32, "LuatOS",  0, eink.font_opposansm12)

    eink.print(16, 64, "中华人民共和国", 0, eink.font_opposansm16_chinese)

    log.debug("before show")

    -- 刷屏幕
    eink.show()
end

sys.taskInit(function()
    eink.model(eink.MODEL_1in54_V2)
    -- eink.setup(1, 0,17,1,4,20)
    eink.setup(1, 0,pin.PB01,pin.PA01,pin.PA04,pin.PB04)-- v0006及以后版本可用pin方式
    eink.setWin(200, 200, 0)
    log.info("eink", "end setup")
    -- 稍微等一会,免得墨水屏没初始化完成
    sys.wait(1000)
    while 1 do
        log.info("e-paper 1.54", "Testing Go\r\n")
        eink154_update()
        log.info("e-paper 1.54", "Testing End\r\n")
        sys.wait(5000) -- 3秒刷新一次
    end
end)
--]]
------------------------------fdb库：kv数据库实验-----------------------------------
--[[
-- fdb库：https://wiki.luatos.com/api/fdb.html
-- kv数据库是指Key-value数据库，是一种以键值对存储数据的一种数据库
sys.taskInit(function()
    sys.wait(1000) -- 免得日志刷了, 生产环境不需要

    -- 检查一下当前固件是否支持fdb
    if not fdb then
        while true do
            log.info("fdb", "this demo need fdb")
            sys.wait(1000)
        end
    end

    -- 初始化kv数据库
    fdb.kvdb_init("onchip_flash")
    log.info("fdb", "init complete")

    -- 先放入一堆值
    local bootime = fdb.kv_get("boottime")  -- 根据key获取对应的数据
    if bootime == nil or type(bootime) ~= "number" then
        bootime = 0
    else
        bootime = bootime + 1
    end

    fdb.kv_set("boottime", bootime)   --设置一对kv数据：1.key名称必填,不能空字符串 2.用户数据,必填,不能nil, 支持字符串/数值/table/布尔值
    fdb.kv_set("my_bool", true)
    fdb.kv_set("my_int", 123)
    fdb.kv_set("my_number", 1.23)
    fdb.kv_set("my_str", "luatos")
    fdb.kv_set("my_table", {name="wendal",age=18})
    fdb.kv_set("my_str_int", "123")
    fdb.kv_set("1", "123")      -- 单字节key
    --fdb.kv_set("my_nil", nil) -- 会提示失败,不支持空值

    -- 根据key获取对应的数据并打印内容
    log.info("fdb", "boottime",      type(fdb.kv_get("boottime")),    fdb.kv_get("boottime"))
    log.info("fdb", "my_bool",      type(fdb.kv_get("my_bool")),    fdb.kv_get("my_bool"))
    log.info("fdb", "my_int",       type(fdb.kv_get("my_int")),     fdb.kv_get("my_int"))
    log.info("fdb", "my_number",    type(fdb.kv_get("my_number")),  fdb.kv_get("my_number"))
    log.info("fdb", "my_str",       type(fdb.kv_get("my_str")),     fdb.kv_get("my_str"))
    log.info("fdb", "my_table",     type(fdb.kv_get("my_table")),   json.encode(fdb.kv_get("my_table")))
    log.info("fdb", "my_str_int",     type(fdb.kv_get("my_str_int")),   fdb.kv_get("my_str_int"))
    log.info("fdb", "1 byte key",     type(fdb.kv_get("1")),   json.encode(fdb.kv_get("1")))

    -- 删除测试
    fdb.kv_del("my_bool")
    local t = fdb.kv_get("my_bool")
    log.info("fdb", "my_bool",      type(t),    t)

    --清空整个kv数据库
    --fdb.kv_clr()
    while true do
        sys.wait(100)
    end
end)
--]]
------------------------------nvm (非易失存储器)实验-------------------------
--[[
require "config"--默认数据文件
--引用nvm库
local nvm = require "nvm"

--先加载初始化数据
nvm.init("config.lua")                 -- 每次更改的数据都会保存在nvm，下一次开机不会丢失，甚至刷固件都不变

--检查一下现在的数据
log.info("nvm","get a",nvm.get("a"))
log.info("nvm","get b",nvm.get("b"))

--改一下看看，下次开机就会是这个值
nvm.set('a',nvm.get("a") + 1)
nvm.set('b',nvm.get("b") + 1)

--看看改之后的数据
log.info("nvm","get a",nvm.get("a"))
log.info("nvm","get b",nvm.get("b"))
--]]
------------------------------SM状态机实验--------------------------------
--[[
--  未成功：需要固件支持
sys.taskInit(function()
    gpio.setup(7, 0, gpio.PULLUP)
    local sm = statem.create()    --指令条数,默认32条  重复执行的次数,0代表不重复,正整数代表具体重复执行的次数.暂不支持永续执行
                :gpio_set(7, 0) -- gpio设置为低电平
                :usleep(10)     -- 休眠10us
                :gpio_set(7, 1) -- gpio设置为高电平
                :gpio_set(7, 0) -- gpio设置为低电平
                :gpio_set(7, 1) -- gpio设置为高电平
                :gpio_set(7, 0) -- gpio设置为低电平
                :gpio_set(7, 1) -- gpio设置为高电平
                :gpio_set(7, 0) -- gpio设置为低电平
                :gpio_set(7, 1) -- gpio设置为高电平
                :usleep(40)     -- 休眠40us
                :finish()
    -- 执行之,后续会支持后台执行
    sm:exec()
    while 1 do
        sys.wait(1000)
    end
end)
--]]
-------------------------(失败) 蓝牙实验-------------------------------------
-- ctrl+K+U取消注释
-- -- 无法找到蓝牙
-- leds = {}
-- if rtos.bsp() == "air101" then -- 与w800/805等价
--     leds["a"] = gpio.setup(pin.PB08, 0, gpio.PULLUP) -- PB_08,输出模式
--     leds["b"] = gpio.setup(pin.PB09, 0, gpio.PULLUP) -- PB_09,输出模式
--     leds["c"] = gpio.setup(pin.PB10, 0, gpio.PULLUP) -- PB_10,输出模式
-- elseif rtos.bsp() == "air103" then -- 与w806等价
--     leds["a"] = gpio.setup(pin.PB24, 0, gpio.PULLUP) -- PB_24,输出模式
--     leds["b"] = gpio.setup(pin.PB25, 0, gpio.PULLUP) -- PB_25,输出模式
--     leds["c"] = gpio.setup(pin.PB26, 0, gpio.PULLUP) -- PB_26,输出模式
-- else
--     log.info("gpio", "pls add gpio.setup for you board")
-- end

-- local count = true
-- local function led_bulingbuling()
--     leds["a"](count == true and 1 or 0)
--     leds["b"](count == true and 1 or 0)
--     leds["c"](count == true and 1 or 0)
--     count = not count
--     nimble.send_msg(1, 0, "led_bulingbuling")
-- end
-- local bulingbuling = sys.timerLoopStart(led_bulingbuling, 1000)

-- if lcd then
--     spi_lcd = spi.deviceSetup(0,20,0,0,8,20*1000*1000,spi.MSB,1,1)
--     lcd.setColor(0x0000,0xFFFF)
--     log.info("lcd.init",
--     lcd.init("st7735s",{port = "device",pin_dc = 17, pin_pwr = 7,pin_rst = 19,direction = 2,w = 160,h = 80,xoffset = 0,yoffset = 24},spi_lcd))
--     lcd.clear()
--     lcd.setFont(lcd.font_opposansm12_chinese)
--     lcd.drawStr(30,15,"nimbledemo",0X07FF)
--     lcd.drawStr(50,35,"监听中",0x001F)
-- else
--     log.info("lcd", "lcd not found, display is off")
-- end

-- gpio.setup(pin.PA00, function(val) print("PA0 R",val) if lcd then lcd.fill(0,40,160,80) if val == 0 then lcd.drawStr(50,60,"R down",0x07E0) end end end, gpio.PULLUP)
-- gpio.setup(pin.PA07, function(val) print("PA7 U",val) if lcd then lcd.fill(0,40,160,80) if  val == 0 then lcd.drawStr(50,60,"U down",0x07E0) end end end, gpio.PULLUP)
-- gpio.setup(pin.PA04, function(val) print("PA4 C",val) if lcd then lcd.fill(0,40,160,80) if  val == 0 then lcd.drawStr(50,60,"C down",0x07E0) end end end, gpio.PULLUP)
-- gpio.setup(pin.PA01, function(val) print("PA1 L",val) if lcd then lcd.fill(0,40,160,80) if  val == 0 then lcd.drawStr(50,60,"L down",0x07E0) end end end, gpio.PULLUP)
-- gpio.setup(pin.PB11, function(val) print("PB11 D",val) if lcd then lcd.fill(0,40,160,80) if  val == 0 then lcd.drawStr(50,60,"D down",0x07E0) end end end, gpio.PULLUP)


-- function decodeURI(s)
--     s = string.gsub(s, '%%(%x%x)', function(h) return string.char(tonumber(h, 16)) end)
--     return s
-- end

-- -- 注册一个命令列表
-- cmds = {
--     -- 控制led的命令
--     led = function(id, val)
--         local led = leds[id]
--         if led then
--             led(val == "on" and 1 or 0)
--         end
--     end,
--     -- 重启板子的命令
--     reboot = function()
--         sys.taskInit(function()
--             log.info("ble", "cmd reboot, after 5s")
--             sys.wait(5000)
--             rtos.reboot()
--         end)
--     end,
--     -- 显示屏输出内容的命令
--     display = function(text)
--         lcd.fill(0, 20, 160, 36)
--         lcd.drawStr(50 , 35, decodeURI(text) ,0x001F)
--     end,
-- }

-- -- 监听BLE主适配的状态变化,需要nimble库
-- if nimble then
--     -- BLE初始化成功或失败会产生该事件
--     sys.subscribe("BLE_STATE_INC", function(state)
--         log.info("ble", "ble state changed", state)
--         if state == 1 then
--             nimble.server_init()
--         else
--             nimble.server_deinit()
--         end
--     end)
    
--     -- 监听GATT服务器的WRITE_CHR事件
--     sys.subscribe("BLE_GATT_WRITE_CHR", function(info, data)
--         sys.timerStop(bulingbuling)
--         -- info 是个table, 但当前没有数据
--         log.info("ble", "data got!!", data:toHex())
--         if data:len() == 0 then
--             return
--         end
--         -- led,a,on 对应hex值 6c65642c612c6f6e
--         -- led,b,on 对应hex值 6c65642c622c6f6e
--         -- led,c,on 对应hex值 6c65642c632c6f6e
--         -- led,a,off 对应 6c65642c612c6f6666
--         -- led,b,off 对应 6c65642c622c6f6666
--         -- led,c,off 对应 6c65642c632c6f6666
--         -- display,xxx 对应 646973706C6179xxx, 支持中文
        
--         local cmd = data:split(",")
--         if cmd[1] and cmds[cmd[1]] then
--             cmds[cmd[1]](table.unpack(cmd, 2))
--         else
--             log.info("ble", "unkown cmd", json.encode(cmd))
--         end
--     end)

    
-- -- TODO 支持传数据(read)和推送数据(notify)

-- -- 配合微信小程序 "LuatOS蓝牙调试"
-- -- 1. 若开发板无天线, 将手机尽量靠近芯片也能搜到
-- -- 2. 该小程序是开源的, 每次write会自动分包
-- -- https://gitee.com/openLuat/luatos-miniapps

--     sys.taskInit(function()
--         sys.wait(2000) -- 为了能看到日志,休眠2秒
--         nimble.debug(6) -- 开启日志
--         nimble.init() -- 初始化nimble, 会产生事件BLE_STATE_INC
--     end)
-- else
--     -- 没有nimble, 那就闪灯吧
--     log.info("gpio", "no nimble, just leds")
-- end 
------------------------------ota 实验---------------------------------
--[[
-- 对于air101/air103来说其实不算真正意义上的ota，仍需要数据线烧录文件，意义不大
local bin_path = "/luadb/"
local bin_name = "update.bin"
local tar_path = "/"

-- ota流程就是把update.bin放在根目录(esp为"/spiffs/" 其余为"/")，重启后会自动升级.
-- update.bin制作方法:luatools中点击生成量产文件,将 SOC量产及远程升级文件 目录中的XXX.ota文件更名为update.bin即可

sys.taskInit(function()
    log.info("-----------------old-------------------")
    local update = io.open(bin_path..bin_name, "rb")
    local update_data = update:read("*a")
    -- log.info(bin_name, "bin_data", update_data)

    local bin = io.open(tar_path..bin_name, "wb")
    bin:write(update_data)
    update:close()
    bin:close()

    rtos.reboot()   
    
    -- local bin = io.open(tar_path..bin_name, "rb")
    -- local bin_data = bin:read("*a")
    -- log.info(bin_name, "bin_data", bin_data)

    while 1 do
        sys.wait(1000)
    end
end)
--]]
------------------------------PWM捕获-----------------------------------












sys.run()


----------------------------学习笔记------------------------------------
--[[
    sys.timerLoopStart(fnc,ms,...)   开启一个循环定时器

    传入值类型  释义
    param      fnc定时器回调函数
    number     ms整数，最大定时126322567ms
    param      ...可变整数fnc的参数

    返回值
    number    定时器ID，如果失败，返回nil
------------------------------------------------------------------------
    pack - 打包和解包格式串
    --https://wiki.luatos.com/api/pack.html?highlight=pack
    --local _, data1, data2, data3, data4, data5, data6 = pack.unpack(data, "b6")
--]]
